Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab7017a3 authored by Bryan Schumaker's avatar Bryan Schumaker Committed by Trond Myklebust
Browse files

NFS: Add version registering framework



This patch adds in the code to track multiple versions of the NFS
protocol.  I created default structures for v2, v3 and v4 so that each
version can continue to work while I convert them into kernel modules.
I also removed the const parameter from the rpc_version array so that I
can change it at runtime.

Signed-off-by: default avatarBryan Schumaker <bjschuma@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent a427b9ec
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -9,8 +9,8 @@ nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
			   write.o namespace.o mount_clnt.o \
			   dns_resolve.o cache_lib.o
nfs-$(CONFIG_ROOT_NFS)	+= nfsroot.o
nfs-$(CONFIG_NFS_V2)	+= proc.o nfs2xdr.o
nfs-$(CONFIG_NFS_V3)	+= nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V2)	+= nfs2super.o proc.o nfs2xdr.o
nfs-$(CONFIG_NFS_V3)	+= nfs3super.o nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V3_ACL)	+= nfs3acl.o
nfs-$(CONFIG_NFS_V4)	+= nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
			   nfs4super.o nfs4file.o delegation.o idmap.o \
+112 −35
Original line number Diff line number Diff line
@@ -51,25 +51,23 @@
#include "internal.h"
#include "fscache.h"
#include "pnfs.h"
#include "nfs.h"
#include "netns.h"

#define NFSDBG_FACILITY		NFSDBG_CLIENT

static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
static DEFINE_SPINLOCK(nfs_version_lock);
static DEFINE_MUTEX(nfs_version_mutex);
static LIST_HEAD(nfs_versions);

/*
 * RPC cruft for NFS
 */
static const struct rpc_version *nfs_version[5] = {
#ifdef CONFIG_NFS_V2
	[2]			= &nfs_version2,
#endif
#ifdef CONFIG_NFS_V3
	[3]			= &nfs_version3,
#endif
#ifdef CONFIG_NFS_V4
	[4]			= &nfs_version4,
#endif
	[2] = NULL,
	[3] = NULL,
	[4] = NULL,
};

const struct rpc_program nfs_program = {
@@ -101,6 +99,93 @@ const struct rpc_program nfsacl_program = {
};
#endif  /* CONFIG_NFS_V3_ACL */

static struct nfs_subversion *find_nfs_version(unsigned int version)
{
	struct nfs_subversion *nfs;
	spin_lock(&nfs_version_lock);

	list_for_each_entry(nfs, &nfs_versions, list) {
		if (nfs->rpc_ops->version == version) {
			spin_unlock(&nfs_version_lock);
			return nfs;
		}
	};

	spin_unlock(&nfs_version_lock);
	return ERR_PTR(-EPROTONOSUPPORT);;
}

struct nfs_subversion *get_nfs_version(unsigned int version)
{
	struct nfs_subversion *nfs = find_nfs_version(version);

	if (IS_ERR(nfs)) {
		mutex_lock(&nfs_version_mutex);
		request_module("nfs%d", version);
		nfs = find_nfs_version(version);
		mutex_unlock(&nfs_version_mutex);
	}

	if (!IS_ERR(nfs))
		try_module_get(nfs->owner);
	return nfs;
}

void put_nfs_version(struct nfs_subversion *nfs)
{
	module_put(nfs->owner);
}

void register_nfs_version(struct nfs_subversion *nfs)
{
	spin_lock(&nfs_version_lock);

	list_add(&nfs->list, &nfs_versions);
	nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers;

	spin_unlock(&nfs_version_lock);
}
EXPORT_SYMBOL_GPL(register_nfs_version);

void unregister_nfs_version(struct nfs_subversion *nfs)
{
	spin_lock(&nfs_version_lock);

	nfs_version[nfs->rpc_ops->version] = NULL;
	list_del(&nfs->list);

	spin_unlock(&nfs_version_lock);
}
EXPORT_SYMBOL_GPL(unregister_nfs_version);

/*
 * Preload all configured NFS versions during module init.
 * This function should be edited after each protocol is converted,
 * and eventually removed.
 */
int __init nfs_register_versions(void)
{
	int err = init_nfs_v2();
	if (err)
		return err;

	err = init_nfs_v3();
	if (err)
		return err;

	return init_nfs_v4();
}

/*
 * Remove each pre-loaded NFS version
 */
void nfs_unregister_versions(void)
{
	exit_nfs_v2();
	exit_nfs_v3();
	exit_nfs_v4();
}

/*
 * Allocate a shared client record
 *
@@ -116,7 +201,10 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
	if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
		goto error_0;

	clp->rpc_ops = cl_init->rpc_ops;
	clp->cl_nfs_mod = cl_init->nfs_mod;
	try_module_get(clp->cl_nfs_mod->owner);

	clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;

	atomic_set(&clp->cl_count, 1);
	clp->cl_cons_state = NFS_CS_INITING;
@@ -145,6 +233,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
	return clp;

error_cleanup:
	put_nfs_version(clp->cl_nfs_mod);
	kfree(clp);
error_0:
	return ERR_PTR(err);
@@ -205,6 +294,7 @@ void nfs_free_client(struct nfs_client *clp)
		put_rpccred(clp->cl_machine_cred);

	put_net(clp->cl_net);
	put_nfs_version(clp->cl_nfs_mod);
	kfree(clp->cl_hostname);
	kfree(clp);

@@ -362,7 +452,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
			continue;

		/* Different NFS versions cannot share the same nfs_client */
		if (clp->rpc_ops != data->rpc_ops)
		if (clp->rpc_ops != data->nfs_mod->rpc_ops)
			continue;

		if (clp->cl_proto != data->proto)
@@ -431,9 +521,10 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
{
	struct nfs_client *clp, *new = NULL;
	struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
	const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;

	dprintk("--> nfs_get_client(%s,v%u)\n",
		cl_init->hostname ?: "", cl_init->rpc_ops->version);
		cl_init->hostname ?: "", rpc_ops->version);

	/* see if the client already exists */
	do {
@@ -450,14 +541,13 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
			list_add(&new->cl_share_link, &nn->nfs_client_list);
			spin_unlock(&nn->nfs_client_lock);
			new->cl_flags = cl_init->init_flags;
			return cl_init->rpc_ops->init_client(new,
						timeparms, ip_addr,
			return rpc_ops->init_client(new, timeparms, ip_addr,
						    authflavour);
		}

		spin_unlock(&nn->nfs_client_lock);

		new = cl_init->rpc_ops->alloc_client(cl_init);
		new = rpc_ops->alloc_client(cl_init);
	} while (!IS_ERR(new));

	dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
@@ -714,13 +804,14 @@ struct nfs_client *nfs_init_client(struct nfs_client *clp,
 * Create a version 2 or 3 client
 */
static int nfs_init_server(struct nfs_server *server,
			   const struct nfs_parsed_mount_data *data)
			   const struct nfs_parsed_mount_data *data,
			   struct nfs_subversion *nfs_mod)
{
	struct nfs_client_initdata cl_init = {
		.hostname = data->nfs_server.hostname,
		.addr = (const struct sockaddr *)&data->nfs_server.address,
		.addrlen = data->nfs_server.addrlen,
		.rpc_ops = NULL,
		.nfs_mod = nfs_mod,
		.proto = data->nfs_server.protocol,
		.net = data->net,
	};
@@ -730,21 +821,6 @@ static int nfs_init_server(struct nfs_server *server,

	dprintk("--> nfs_init_server()\n");

	switch (data->version) {
#ifdef CONFIG_NFS_V2
	case 2:
		cl_init.rpc_ops = &nfs_v2_clientops;
		break;
#endif
#ifdef CONFIG_NFS_V3
	case 3:
		cl_init.rpc_ops = &nfs_v3_clientops;
		break;
#endif
	default:
		return -EPROTONOSUPPORT;
	}

	nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
			data->timeo, data->retrans);
	if (data->flags & NFS_MOUNT_NORESVPORT)
@@ -1033,7 +1109,8 @@ void nfs_free_server(struct nfs_server *server)
 * - keyed on server and FSID
 */
struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
				     struct nfs_fh *mntfh)
				     struct nfs_fh *mntfh,
				     struct nfs_subversion *nfs_mod)
{
	struct nfs_server *server;
	struct nfs_fattr *fattr;
@@ -1049,7 +1126,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
		goto error;

	/* Get a client representation */
	error = nfs_init_server(server, data);
	error = nfs_init_server(server, data, nfs_mod);
	if (error < 0)
		goto error;

+3 −6
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#include "fscache.h"
#include "dns_resolve.h"
#include "pnfs.h"
#include "nfs.h"
#include "netns.h"

#define NFSDBG_FACILITY		NFSDBG_VFS
@@ -1671,21 +1672,17 @@ static int __init init_nfs_fs(void)
	rpc_proc_register(&init_net, &nfs_rpcstat);
#endif

#ifdef CONFIG_NFS_V4
	err = init_nfs_v4();
	err = nfs_register_versions();
	if (err)
		goto out1;
#endif

	if ((err = register_nfs_fs()) != 0)
		goto out0;

	return 0;
out0:
#ifdef CONFIG_NFS_V4
	exit_nfs_v4();
	nfs_unregister_versions();
out1:
#endif
#ifdef CONFIG_PROC_FS
	rpc_proc_unregister(&init_net, "nfs");
#endif
+6 −4
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ struct nfs_client_initdata {
	const char *hostname;
	const struct sockaddr *addr;
	size_t addrlen;
	const struct nfs_rpc_ops *rpc_ops;
	struct nfs_subversion *nfs_mod;
	int proto;
	u32 minorversion;
	struct net *net;
@@ -189,7 +189,8 @@ nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
				struct nfs4_sessionid *);
extern struct nfs_server *nfs_create_server(
					const struct nfs_parsed_mount_data *,
					struct nfs_fh *);
					struct nfs_fh *,
					struct nfs_subversion *);
extern struct nfs_server *nfs4_create_server(
					const struct nfs_parsed_mount_data *,
					struct nfs_fh *);
@@ -321,6 +322,7 @@ void nfs_zap_acl_cache(struct inode *inode);
extern int nfs_wait_bit_killable(void *word);

/* super.c */
extern struct file_system_type nfs_fs_type;
extern struct file_system_type nfs_xdev_fs_type;
#ifdef CONFIG_NFS_V4
extern struct file_system_type nfs4_xdev_fs_type;
@@ -329,8 +331,8 @@ extern struct file_system_type nfs4_referral_fs_type;
void nfs_initialise_sb(struct super_block *);
int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
struct dentry *nfs_fs_mount_common(struct file_system_type *, struct nfs_server *,
				   int, const char *, struct nfs_mount_info *);
struct dentry *nfs_fs_mount_common(struct nfs_server *, int, const char *,
				   struct nfs_mount_info *, struct nfs_subversion *);
struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *);
struct dentry * nfs_xdev_mount_common(struct file_system_type *, int,
		const char *, struct nfs_mount_info *);

fs/nfs/nfs.h

0 → 100644
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2012 Netapp, Inc. All rights reserved.
 *
 * Function and structures exported by the NFS module
 * for use by NFS version-specific modules.
 */
#ifndef __LINUX_INTERNAL_NFS_H
#define __LINUX_INTERNAL_NFS_H

#include <linux/fs.h>
#include <linux/sunrpc/sched.h>
#include <linux/nfs_xdr.h>

struct nfs_subversion {
	struct module *owner;	/* THIS_MODULE pointer */
	struct file_system_type *nfs_fs;	/* NFS filesystem type */
	const struct rpc_version *rpc_vers;	/* NFS version information */
	const struct nfs_rpc_ops *rpc_ops;	/* NFS operations */
	struct list_head list;		/* List of NFS versions */
};

int nfs_register_versions(void);
void nfs_unregister_versions(void);

#ifdef CONFIG_NFS_V2
int init_nfs_v2(void);
void exit_nfs_v2(void);
#else /* CONFIG_NFS_V2 */
static inline int __init init_nfs_v2(void)
{
	return 0;
}

static inline void exit_nfs_v2(void)
{
}
#endif /* CONFIG_NFS_V2 */

#ifdef CONFIG_NFS_V3
int init_nfs_v3(void);
void exit_nfs_v3(void);
#else /* CONFIG_NFS_V3 */
static inline int __init init_nfs_v3(void)
{
	return 0;
}

static inline void exit_nfs_v3(void)
{
}
#endif /* CONFIG_NFS_V3 */

#ifdef CONFIG_NFS_V4
int init_nfs_v4(void);
void exit_nfs_v4(void);
#else /* CONFIG_NFS_V4 */
static inline int __init init_nfs_v4(void)
{
	return 0;
}

static inline void exit_nfs_v4(void)
{
}
#endif /* CONFIG_NFS_V4 */

struct nfs_subversion *get_nfs_version(unsigned int);
void put_nfs_version(struct nfs_subversion *);
void register_nfs_version(struct nfs_subversion *);
void unregister_nfs_version(struct nfs_subversion *);

#endif /* __LINUX_INTERNAL_NFS_H */
Loading