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

Commit f044c884 authored by David Howells's avatar David Howells
Browse files

afs: Lay the groundwork for supporting network namespaces



Lay the groundwork for supporting network namespaces (netns) to the AFS
filesystem by moving various global features to a network-namespace struct
(afs_net) and providing an instance of this as a temporary global variable
that everything uses via accessor functions for the moment.

The following changes have been made:

 (1) Store the netns in the superblock info.  This will be obtained from
     the mounter's nsproxy on a manual mount and inherited from the parent
     superblock on an automount.

 (2) The cell list is made per-netns.  It can be viewed through
     /proc/net/afs/cells and also be modified by writing commands to that
     file.

 (3) The local workstation cell is set per-ns in /proc/net/afs/rootcell.
     This is unset by default.

 (4) The 'rootcell' module parameter, which sets a cell and VL server list
     modifies the init net namespace, thereby allowing an AFS root fs to be
     theoretically used.

 (5) The volume location lists and the file lock manager are made
     per-netns.

 (6) The AF_RXRPC socket and associated I/O bits are made per-ns.

The various workqueues remain global for the moment.

Changes still to be made:

 (1) /proc/fs/afs/ should be moved to /proc/net/afs/ and a symlink emplaced
     from the old name.

 (2) A per-netns subsys needs to be registered for AFS into which it can
     store its per-netns data.

 (3) Rather than the AF_RXRPC socket being opened on module init, it needs
     to be opened on the creation of a superblock in that netns.

 (4) The socket needs to be closed when the last superblock using it is
     destroyed and all outstanding client calls on it have been completed.
     This prevents a reference loop on the namespace.

 (5) It is possible that several namespaces will want to use AFS, in which
     case each one will need its own UDP port.  These can either be set
     through /proc/net/afs/cm_port or the kernel can pick one at random.
     The init_ns gets 7001 by default.

Other issues that need resolving:

 (1) The DNS keyring needs net-namespacing.

 (2) Where do upcalls go (eg. DNS request-key upcall)?

 (3) Need something like open_socket_in_file_ns() syscall so that AFS
     command line tools attempting to operate on an AFS file/volume have
     their RPC calls go to the right place.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 5e4def20
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -72,6 +72,15 @@ struct afs_callback {

#define AFSCBMAX 50	/* maximum callbacks transferred per bulk op */

struct afs_uuid {
	__be32		time_low;			/* low part of timestamp */
	__be16		time_mid;			/* mid part of timestamp */
	__be16		time_hi_and_version;		/* high part of timestamp and version  */
	__u8		clock_seq_hi_and_reserved;	/* clock seq hi and variant */
	__u8		clock_seq_low;			/* clock seq low */
	__u8		node[6];			/* spatially unique node ID (MAC addr) */
};

/*
 * AFS volume information
 */
+2 −22
Original line number Diff line number Diff line
@@ -28,9 +28,7 @@ unsigned afs_vnode_update_timeout = 10;
	CIRC_SPACE((server)->cb_break_head, (server)->cb_break_tail,	\
		   ARRAY_SIZE((server)->cb_break))

//static void afs_callback_updater(struct work_struct *);

static struct workqueue_struct *afs_callback_update_worker;
struct workqueue_struct *afs_callback_update_worker;

/*
 * allow the fileserver to request callback state (re-)initialisation
@@ -343,7 +341,7 @@ void afs_dispatch_give_up_callbacks(struct work_struct *work)
	 *   had callbacks entirely, and the server will call us later to break
	 *   them
	 */
	afs_fs_give_up_callbacks(server, true);
	afs_fs_give_up_callbacks(server->cell->net, server, true);
}

/*
@@ -456,21 +454,3 @@ static void afs_callback_updater(struct work_struct *work)
	afs_put_vnode(vl);
}
#endif

/*
 * initialise the callback update process
 */
int __init afs_callback_update_init(void)
{
	afs_callback_update_worker = alloc_ordered_workqueue("kafs_callbackd",
							     WQ_MEM_RECLAIM);
	return afs_callback_update_worker ? 0 : -ENOMEM;
}

/*
 * shut down the callback update process
 */
void afs_callback_update_kill(void)
{
	destroy_workqueue(afs_callback_update_worker);
}
+63 −67
Original line number Diff line number Diff line
@@ -18,20 +18,12 @@
#include <keys/rxrpc-type.h>
#include "internal.h"

DECLARE_RWSEM(afs_proc_cells_sem);
LIST_HEAD(afs_proc_cells);

static LIST_HEAD(afs_cells);
static DEFINE_RWLOCK(afs_cells_lock);
static DECLARE_RWSEM(afs_cells_sem); /* add/remove serialisation */
static DECLARE_WAIT_QUEUE_HEAD(afs_cells_freeable_wq);
static struct afs_cell *afs_cell_root;

/*
 * allocate a cell record and fill in its name, VL server address list and
 * allocate an anonymous key
 */
static struct afs_cell *afs_cell_alloc(const char *name, unsigned namelen,
static struct afs_cell *afs_cell_alloc(struct afs_net *net,
				       const char *name, unsigned namelen,
				       char *vllist)
{
	struct afs_cell *cell;
@@ -62,6 +54,7 @@ static struct afs_cell *afs_cell_alloc(const char *name, unsigned namelen,

	atomic_set(&cell->usage, 1);
	INIT_LIST_HEAD(&cell->link);
	cell->net = net;
	rwlock_init(&cell->servers_lock);
	INIT_LIST_HEAD(&cell->servers);
	init_rwsem(&cell->vl_sem);
@@ -142,12 +135,14 @@ static struct afs_cell *afs_cell_alloc(const char *name, unsigned namelen,

/*
 * afs_cell_crate() - create a cell record
 * @net:	The network namespace
 * @name:	is the name of the cell.
 * @namsesz:	is the strlen of the cell name.
 * @vllist:	is a colon separated list of IP addresses in "a.b.c.d" format.
 * @retref:	is T to return the cell reference when the cell exists.
 */
struct afs_cell *afs_cell_create(const char *name, unsigned namesz,
struct afs_cell *afs_cell_create(struct afs_net *net,
				 const char *name, unsigned namesz,
				 char *vllist, bool retref)
{
	struct afs_cell *cell;
@@ -155,23 +150,23 @@ struct afs_cell *afs_cell_create(const char *name, unsigned namesz,

	_enter("%*.*s,%s", namesz, namesz, name ?: "", vllist);

	down_write(&afs_cells_sem);
	read_lock(&afs_cells_lock);
	list_for_each_entry(cell, &afs_cells, link) {
	down_write(&net->cells_sem);
	read_lock(&net->cells_lock);
	list_for_each_entry(cell, &net->cells, link) {
		if (strncasecmp(cell->name, name, namesz) == 0)
			goto duplicate_name;
	}
	read_unlock(&afs_cells_lock);
	read_unlock(&net->cells_lock);

	cell = afs_cell_alloc(name, namesz, vllist);
	cell = afs_cell_alloc(net, name, namesz, vllist);
	if (IS_ERR(cell)) {
		_leave(" = %ld", PTR_ERR(cell));
		up_write(&afs_cells_sem);
		up_write(&net->cells_sem);
		return cell;
	}

	/* add a proc directory for this cell */
	ret = afs_proc_cell_setup(cell);
	ret = afs_proc_cell_setup(net, cell);
	if (ret < 0)
		goto error;

@@ -183,20 +178,20 @@ struct afs_cell *afs_cell_create(const char *name, unsigned namesz,
#endif

	/* add to the cell lists */
	write_lock(&afs_cells_lock);
	list_add_tail(&cell->link, &afs_cells);
	write_unlock(&afs_cells_lock);
	write_lock(&net->cells_lock);
	list_add_tail(&cell->link, &net->cells);
	write_unlock(&net->cells_lock);

	down_write(&afs_proc_cells_sem);
	list_add_tail(&cell->proc_link, &afs_proc_cells);
	up_write(&afs_proc_cells_sem);
	up_write(&afs_cells_sem);
	down_write(&net->proc_cells_sem);
	list_add_tail(&cell->proc_link, &net->proc_cells);
	up_write(&net->proc_cells_sem);
	up_write(&net->cells_sem);

	_leave(" = %p", cell);
	return cell;

error:
	up_write(&afs_cells_sem);
	up_write(&net->cells_sem);
	key_put(cell->anonymous_key);
	kfree(cell);
	_leave(" = %d", ret);
@@ -206,8 +201,8 @@ struct afs_cell *afs_cell_create(const char *name, unsigned namesz,
	if (retref && !IS_ERR(cell))
		afs_get_cell(cell);

	read_unlock(&afs_cells_lock);
	up_write(&afs_cells_sem);
	read_unlock(&net->cells_lock);
	up_write(&net->cells_sem);

	if (retref) {
		_leave(" = %p", cell);
@@ -223,7 +218,7 @@ struct afs_cell *afs_cell_create(const char *name, unsigned namesz,
 * - can be called with a module parameter string
 * - can be called from a write to /proc/fs/afs/rootcell
 */
int afs_cell_init(char *rootcell)
int afs_cell_init(struct afs_net *net, char *rootcell)
{
	struct afs_cell *old_root, *new_root;
	char *cp;
@@ -245,17 +240,17 @@ int afs_cell_init(char *rootcell)
		*cp++ = 0;

	/* allocate a cell record for the root cell */
	new_root = afs_cell_create(rootcell, strlen(rootcell), cp, false);
	new_root = afs_cell_create(net, rootcell, strlen(rootcell), cp, false);
	if (IS_ERR(new_root)) {
		_leave(" = %ld", PTR_ERR(new_root));
		return PTR_ERR(new_root);
	}

	/* install the new cell */
	write_lock(&afs_cells_lock);
	old_root = afs_cell_root;
	afs_cell_root = new_root;
	write_unlock(&afs_cells_lock);
	write_lock(&net->cells_lock);
	old_root = net->ws_cell;
	net->ws_cell = new_root;
	write_unlock(&net->cells_lock);
	afs_put_cell(old_root);

	_leave(" = 0");
@@ -265,19 +260,20 @@ int afs_cell_init(char *rootcell)
/*
 * lookup a cell record
 */
struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz,
struct afs_cell *afs_cell_lookup(struct afs_net *net,
				 const char *name, unsigned namesz,
				 bool dns_cell)
{
	struct afs_cell *cell;

	_enter("\"%*.*s\",", namesz, namesz, name ?: "");

	down_read(&afs_cells_sem);
	read_lock(&afs_cells_lock);
	down_read(&net->cells_sem);
	read_lock(&net->cells_lock);

	if (name) {
		/* if the cell was named, look for it in the cell record list */
		list_for_each_entry(cell, &afs_cells, link) {
		list_for_each_entry(cell, &net->cells, link) {
			if (strncmp(cell->name, name, namesz) == 0) {
				afs_get_cell(cell);
				goto found;
@@ -289,7 +285,7 @@ struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz,
	found:
		;
	} else {
		cell = afs_cell_root;
		cell = net->ws_cell;
		if (!cell) {
			/* this should not happen unless user tries to mount
			 * when root cell is not set. Return an impossibly
@@ -304,16 +300,16 @@ struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz,

	}

	read_unlock(&afs_cells_lock);
	up_read(&afs_cells_sem);
	read_unlock(&net->cells_lock);
	up_read(&net->cells_sem);
	_leave(" = %p", cell);
	return cell;

create_cell:
	read_unlock(&afs_cells_lock);
	up_read(&afs_cells_sem);
	read_unlock(&net->cells_lock);
	up_read(&net->cells_sem);

	cell = afs_cell_create(name, namesz, NULL, true);
	cell = afs_cell_create(net, name, namesz, NULL, true);

	_leave(" = %p", cell);
	return cell;
@@ -325,14 +321,14 @@ struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz,
 */
struct afs_cell *afs_get_cell_maybe(struct afs_cell *cell)
{
	write_lock(&afs_cells_lock);
	write_lock(&net->cells_lock);

	if (cell && !list_empty(&cell->link))
		afs_get_cell(cell);
	else
		cell = NULL;

	write_unlock(&afs_cells_lock);
	write_unlock(&net->cells_lock);
	return cell;
}
#endif  /*  0  */
@@ -351,10 +347,10 @@ void afs_put_cell(struct afs_cell *cell)

	/* to prevent a race, the decrement and the dequeue must be effectively
	 * atomic */
	write_lock(&afs_cells_lock);
	write_lock(&cell->net->cells_lock);

	if (likely(!atomic_dec_and_test(&cell->usage))) {
		write_unlock(&afs_cells_lock);
		write_unlock(&cell->net->cells_lock);
		_leave("");
		return;
	}
@@ -362,19 +358,19 @@ void afs_put_cell(struct afs_cell *cell)
	ASSERT(list_empty(&cell->servers));
	ASSERT(list_empty(&cell->vl_list));

	write_unlock(&afs_cells_lock);
	wake_up(&cell->net->cells_freeable_wq);

	wake_up(&afs_cells_freeable_wq);
	write_unlock(&cell->net->cells_lock);

	_leave(" [unused]");
}

/*
 * destroy a cell record
 * - must be called with the afs_cells_sem write-locked
 * - must be called with the net->cells_sem write-locked
 * - cell->link should have been broken by the caller
 */
static void afs_cell_destroy(struct afs_cell *cell)
static void afs_cell_destroy(struct afs_net *net, struct afs_cell *cell)
{
	_enter("%p{%d,%s}", cell, atomic_read(&cell->usage), cell->name);

@@ -387,14 +383,14 @@ static void afs_cell_destroy(struct afs_cell *cell)

		_debug("wait for cell %s", cell->name);
		set_current_state(TASK_UNINTERRUPTIBLE);
		add_wait_queue(&afs_cells_freeable_wq, &myself);
		add_wait_queue(&net->cells_freeable_wq, &myself);

		while (atomic_read(&cell->usage) > 0) {
			schedule();
			set_current_state(TASK_UNINTERRUPTIBLE);
		}

		remove_wait_queue(&afs_cells_freeable_wq, &myself);
		remove_wait_queue(&net->cells_freeable_wq, &myself);
		set_current_state(TASK_RUNNING);
	}

@@ -403,11 +399,11 @@ static void afs_cell_destroy(struct afs_cell *cell)
	ASSERT(list_empty(&cell->servers));
	ASSERT(list_empty(&cell->vl_list));

	afs_proc_cell_remove(cell);
	afs_proc_cell_remove(net, cell);

	down_write(&afs_proc_cells_sem);
	down_write(&net->proc_cells_sem);
	list_del_init(&cell->proc_link);
	up_write(&afs_proc_cells_sem);
	up_write(&net->proc_cells_sem);

#ifdef CONFIG_AFS_FSCACHE
	fscache_relinquish_cookie(cell->cache, 0);
@@ -422,39 +418,39 @@ static void afs_cell_destroy(struct afs_cell *cell)
 * purge in-memory cell database on module unload or afs_init() failure
 * - the timeout daemon is stopped before calling this
 */
void afs_cell_purge(void)
void afs_cell_purge(struct afs_net *net)
{
	struct afs_cell *cell;

	_enter("");

	afs_put_cell(afs_cell_root);
	afs_put_cell(net->ws_cell);

	down_write(&afs_cells_sem);
	down_write(&net->cells_sem);

	while (!list_empty(&afs_cells)) {
	while (!list_empty(&net->cells)) {
		cell = NULL;

		/* remove the next cell from the front of the list */
		write_lock(&afs_cells_lock);
		write_lock(&net->cells_lock);

		if (!list_empty(&afs_cells)) {
			cell = list_entry(afs_cells.next,
		if (!list_empty(&net->cells)) {
			cell = list_entry(net->cells.next,
					  struct afs_cell, link);
			list_del_init(&cell->link);
		}

		write_unlock(&afs_cells_lock);
		write_unlock(&net->cells_lock);

		if (cell) {
			_debug("PURGING CELL %s (%d)",
			       cell->name, atomic_read(&cell->usage));

			/* now the cell should be left with no references */
			afs_cell_destroy(cell);
			afs_cell_destroy(net, cell);
		}
	}

	up_write(&afs_cells_sem);
	up_write(&net->cells_sem);
	_leave("");
}
+13 −13
Original line number Diff line number Diff line
@@ -193,7 +193,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)

	switch (call->unmarshall) {
	case 0:
		rxrpc_kernel_get_peer(afs_socket, call->rxcall, &srx);
		rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
		call->offset = 0;
		call->unmarshall++;

@@ -290,7 +290,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)

	/* we'll need the file server record as that tells us which set of
	 * vnodes to operate upon */
	server = afs_find_server(&srx);
	server = afs_find_server(call->net, &srx);
	if (!server)
		return -ENOTCONN;
	call->server = server;
@@ -324,7 +324,7 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)

	_enter("");

	rxrpc_kernel_get_peer(afs_socket, call->rxcall, &srx);
	rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);

	ret = afs_extract_data(call, NULL, 0, false);
	if (ret < 0)
@@ -335,7 +335,7 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)

	/* we'll need the file server record as that tells us which set of
	 * vnodes to operate upon */
	server = afs_find_server(&srx);
	server = afs_find_server(call->net, &srx);
	if (!server)
		return -ENOTCONN;
	call->server = server;
@@ -357,7 +357,7 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)

	_enter("");

	rxrpc_kernel_get_peer(afs_socket, call->rxcall, &srx);
	rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);

	_enter("{%u}", call->unmarshall);

@@ -407,7 +407,7 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)

	/* we'll need the file server record as that tells us which set of
	 * vnodes to operate upon */
	server = afs_find_server(&srx);
	server = afs_find_server(call->net, &srx);
	if (!server)
		return -ENOTCONN;
	call->server = server;
@@ -461,7 +461,7 @@ static void SRXAFSCB_ProbeUuid(struct work_struct *work)

	_enter("");

	if (memcmp(r, &afs_uuid, sizeof(afs_uuid)) == 0)
	if (memcmp(r, &call->net->uuid, sizeof(call->net->uuid)) == 0)
		reply.match = htonl(0);
	else
		reply.match = htonl(1);
@@ -568,13 +568,13 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work)
	memset(&reply, 0, sizeof(reply));
	reply.ia.nifs = htonl(nifs);

	reply.ia.uuid[0] = afs_uuid.time_low;
	reply.ia.uuid[1] = htonl(ntohs(afs_uuid.time_mid));
	reply.ia.uuid[2] = htonl(ntohs(afs_uuid.time_hi_and_version));
	reply.ia.uuid[3] = htonl((s8) afs_uuid.clock_seq_hi_and_reserved);
	reply.ia.uuid[4] = htonl((s8) afs_uuid.clock_seq_low);
	reply.ia.uuid[0] = call->net->uuid.time_low;
	reply.ia.uuid[1] = htonl(ntohs(call->net->uuid.time_mid));
	reply.ia.uuid[2] = htonl(ntohs(call->net->uuid.time_hi_and_version));
	reply.ia.uuid[3] = htonl((s8) call->net->uuid.clock_seq_hi_and_reserved);
	reply.ia.uuid[4] = htonl((s8) call->net->uuid.clock_seq_low);
	for (loop = 0; loop < 6; loop++)
		reply.ia.uuid[loop + 5] = htonl((s8) afs_uuid.node[loop]);
		reply.ia.uuid[loop + 5] = htonl((s8) call->net->uuid.node[loop]);

	if (ifs) {
		for (loop = 0; loop < nifs; loop++) {
+2 −37
Original line number Diff line number Diff line
@@ -14,47 +14,16 @@
#define AFS_LOCK_GRANTED	0
#define AFS_LOCK_PENDING	1

struct workqueue_struct *afs_lock_manager;

static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl);
static void afs_fl_release_private(struct file_lock *fl);

static struct workqueue_struct *afs_lock_manager;
static DEFINE_MUTEX(afs_lock_manager_mutex);

static const struct file_lock_operations afs_lock_ops = {
	.fl_copy_lock		= afs_fl_copy_lock,
	.fl_release_private	= afs_fl_release_private,
};

/*
 * initialise the lock manager thread if it isn't already running
 */
static int afs_init_lock_manager(void)
{
	int ret;

	ret = 0;
	if (!afs_lock_manager) {
		mutex_lock(&afs_lock_manager_mutex);
		if (!afs_lock_manager) {
			afs_lock_manager = alloc_workqueue("kafs_lockd",
							   WQ_MEM_RECLAIM, 0);
			if (!afs_lock_manager)
				ret = -ENOMEM;
		}
		mutex_unlock(&afs_lock_manager_mutex);
	}
	return ret;
}

/*
 * destroy the lock manager thread if it's running
 */
void __exit afs_kill_lock_manager(void)
{
	if (afs_lock_manager)
		destroy_workqueue(afs_lock_manager);
}

/*
 * if the callback is broken on this vnode, then the lock may now be available
 */
@@ -264,10 +233,6 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
	if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX)
		return -EINVAL;

	ret = afs_init_lock_manager();
	if (ret < 0)
		return ret;

	fl->fl_ops = &afs_lock_ops;
	INIT_LIST_HEAD(&fl->fl_u.afs.link);
	fl->fl_u.afs.state = AFS_LOCK_PENDING;
Loading