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

Commit 2127d82a authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4: Convert idmapper to use the new framework for pipefs dentries

parent 5f42b016
Loading
Loading
Loading
Loading
+34 −150
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ struct idmap_legacy_upcalldata {
};

struct idmap {
	struct rpc_pipe_dir_object idmap_pdo;
	struct rpc_pipe		*idmap_pipe;
	struct idmap_legacy_upcalldata *idmap_upcall_data;
	struct mutex		idmap_mutex;
@@ -402,18 +403,23 @@ static struct key_type key_type_id_resolver_legacy = {
	.request_key	= nfs_idmap_legacy_upcall,
};

static void __nfs_idmap_unregister(struct rpc_pipe *pipe)
static void nfs_idmap_pipe_destroy(struct dentry *dir,
		struct rpc_pipe_dir_object *pdo)
{
	struct idmap *idmap = pdo->pdo_data;
	struct rpc_pipe *pipe = idmap->idmap_pipe;

	if (pipe->dentry) {
		rpc_unlink(pipe->dentry);
		pipe->dentry = NULL;
	}
}

static int __nfs_idmap_register(struct dentry *dir,
				     struct idmap *idmap,
				     struct rpc_pipe *pipe)
static int nfs_idmap_pipe_create(struct dentry *dir,
		struct rpc_pipe_dir_object *pdo)
{
	struct idmap *idmap = pdo->pdo_data;
	struct rpc_pipe *pipe = idmap->idmap_pipe;
	struct dentry *dentry;

	dentry = rpc_mkpipe_dentry(dir, "idmap", idmap, pipe);
@@ -423,36 +429,10 @@ static int __nfs_idmap_register(struct dentry *dir,
	return 0;
}

static void nfs_idmap_unregister(struct nfs_client *clp,
				      struct rpc_pipe *pipe)
{
	struct net *net = clp->cl_net;
	struct super_block *pipefs_sb;

	pipefs_sb = rpc_get_sb_net(net);
	if (pipefs_sb) {
		__nfs_idmap_unregister(pipe);
		rpc_put_sb_net(net);
	}
}

static int nfs_idmap_register(struct nfs_client *clp,
				   struct idmap *idmap,
				   struct rpc_pipe *pipe)
{
	struct net *net = clp->cl_net;
	struct super_block *pipefs_sb;
	int err = 0;

	pipefs_sb = rpc_get_sb_net(net);
	if (pipefs_sb) {
		if (clp->cl_rpcclient->cl_dentry)
			err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
						   idmap, pipe);
		rpc_put_sb_net(net);
	}
	return err;
}
static const struct rpc_pipe_dir_object_ops nfs_idmap_pipe_dir_object_ops = {
	.create = nfs_idmap_pipe_create,
	.destroy = nfs_idmap_pipe_destroy,
};

int
nfs_idmap_new(struct nfs_client *clp)
@@ -465,23 +445,31 @@ nfs_idmap_new(struct nfs_client *clp)
	if (idmap == NULL)
		return -ENOMEM;

	rpc_init_pipe_dir_object(&idmap->idmap_pdo,
			&nfs_idmap_pipe_dir_object_ops,
			idmap);

	pipe = rpc_mkpipe_data(&idmap_upcall_ops, 0);
	if (IS_ERR(pipe)) {
		error = PTR_ERR(pipe);
		kfree(idmap);
		return error;
	}
	error = nfs_idmap_register(clp, idmap, pipe);
	if (error) {
		rpc_destroy_pipe_data(pipe);
		kfree(idmap);
		return error;
		goto err;
	}
	idmap->idmap_pipe = pipe;
	mutex_init(&idmap->idmap_mutex);

	error = rpc_add_pipe_dir_object(clp->cl_net,
			&clp->cl_rpcclient->cl_pipedir_objects,
			&idmap->idmap_pdo);
	if (error)
		goto err_destroy_pipe;

	clp->cl_idmap = idmap;
	return 0;
err_destroy_pipe:
	rpc_destroy_pipe_data(idmap->idmap_pipe);
err:
	kfree(idmap);
	return error;
}

void
@@ -491,130 +479,26 @@ nfs_idmap_delete(struct nfs_client *clp)

	if (!idmap)
		return;
	nfs_idmap_unregister(clp, idmap->idmap_pipe);
	rpc_destroy_pipe_data(idmap->idmap_pipe);
	clp->cl_idmap = NULL;
	rpc_remove_pipe_dir_object(clp->cl_net,
			&clp->cl_rpcclient->cl_pipedir_objects,
			&idmap->idmap_pdo);
	rpc_destroy_pipe_data(idmap->idmap_pipe);
	kfree(idmap);
}

static int __rpc_pipefs_event(struct nfs_client *clp, unsigned long event,
			      struct super_block *sb)
{
	int err = 0;

	switch (event) {
	case RPC_PIPEFS_MOUNT:
		err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
						clp->cl_idmap,
						clp->cl_idmap->idmap_pipe);
		break;
	case RPC_PIPEFS_UMOUNT:
		if (clp->cl_idmap->idmap_pipe) {
			struct dentry *parent;

			parent = clp->cl_idmap->idmap_pipe->dentry->d_parent;
			__nfs_idmap_unregister(clp->cl_idmap->idmap_pipe);
			/*
			 * Note: This is a dirty hack. SUNRPC hook has been
			 * called already but simple_rmdir() call for the
			 * directory returned with error because of idmap pipe
			 * inside. Thus now we have to remove this directory
			 * here.
			 */
			if (rpc_rmdir(parent))
				printk(KERN_ERR "NFS: %s: failed to remove "
					"clnt dir!\n", __func__);
		}
		break;
	default:
		printk(KERN_ERR "NFS: %s: unknown event: %ld\n", __func__,
			event);
		return -ENOTSUPP;
	}
	return err;
}

static struct nfs_client *nfs_get_client_for_event(struct net *net, int event)
{
	struct nfs_net *nn = net_generic(net, nfs_net_id);
	struct dentry *cl_dentry;
	struct nfs_client *clp;
	int err;

restart:
	spin_lock(&nn->nfs_client_lock);
	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
		/* Wait for initialisation to finish */
		if (clp->cl_cons_state == NFS_CS_INITING) {
			atomic_inc(&clp->cl_count);
			spin_unlock(&nn->nfs_client_lock);
			err = nfs_wait_client_init_complete(clp);
			nfs_put_client(clp);
			if (err)
				return NULL;
			goto restart;
		}
		/* Skip nfs_clients that failed to initialise */
		if (clp->cl_cons_state < 0)
			continue;
		smp_rmb();
		if (clp->rpc_ops != &nfs_v4_clientops)
			continue;
		cl_dentry = clp->cl_idmap->idmap_pipe->dentry;
		if (((event == RPC_PIPEFS_MOUNT) && cl_dentry) ||
		    ((event == RPC_PIPEFS_UMOUNT) && !cl_dentry))
			continue;
		atomic_inc(&clp->cl_count);
		spin_unlock(&nn->nfs_client_lock);
		return clp;
	}
	spin_unlock(&nn->nfs_client_lock);
	return NULL;
}

static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event,
			    void *ptr)
{
	struct super_block *sb = ptr;
	struct nfs_client *clp;
	int error = 0;

	if (!try_module_get(THIS_MODULE))
		return 0;

	while ((clp = nfs_get_client_for_event(sb->s_fs_info, event))) {
		error = __rpc_pipefs_event(clp, event, sb);
		nfs_put_client(clp);
		if (error)
			break;
	}
	module_put(THIS_MODULE);
	return error;
}

#define PIPEFS_NFS_PRIO		1

static struct notifier_block nfs_idmap_block = {
	.notifier_call	= rpc_pipefs_event,
	.priority	= SUNRPC_PIPEFS_NFS_PRIO,
};

int nfs_idmap_init(void)
{
	int ret;
	ret = nfs_idmap_init_keyring();
	if (ret != 0)
		goto out;
	ret = rpc_pipefs_notifier_register(&nfs_idmap_block);
	if (ret != 0)
		nfs_idmap_quit_keyring();
out:
	return ret;
}

void nfs_idmap_quit(void)
{
	rpc_pipefs_notifier_unregister(&nfs_idmap_block);
	nfs_idmap_quit_keyring();
}