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

Commit 989782dc authored by David Howells's avatar David Howells
Browse files

afs: Overhaul cell database management



Overhaul the way that the in-kernel AFS client keeps track of cells in the
following manner:

 (1) Cells are now held in an rbtree to make walking them quicker and RCU
     managed (though this is probably overkill).

 (2) Cells now have a manager work item that:

     (A) Looks after fetching and refreshing the VL server list.

     (B) Manages cell record lifetime, including initialising and
     	 destruction.

     (B) Manages cell record caching whereby threads are kept around for a
     	 certain time after last use and then destroyed.

     (C) Manages the FS-Cache index cookie for a cell.  It is not permitted
     	 for a cookie to be in use twice, so we have to be careful to not
     	 allow a new cell record to exist at the same time as an old record
     	 of the same name.

 (3) Each AFS network namespace is given a manager work item that manages
     the cells within it, maintaining a single timer to prod cells into
     updating their DNS records.

     This uses the reduce_timer() facility to make the timer expire at the
     soonest timed event that needs happening.

 (4) When a module is being unloaded, cells and cell managers are now
     counted out using dec_after_work() to make sure the module text is
     pinned until after the data structures have been cleaned up.

 (5) Each cell's VL server list is now protected by a seqlock rather than a
     semaphore.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent be080a6f
Loading
Loading
Loading
Loading
+634 −282

File changed.

Preview size limit exceeded, changes collapsed.

+45 −15
Original line number Diff line number Diff line
@@ -207,13 +207,14 @@ struct afs_net {
	atomic_t		nr_superblocks;

	/* Cell database */
	struct list_head	cells;
	struct rb_root		cells;
	struct afs_cell		*ws_cell;
	rwlock_t		cells_lock;
	struct rw_semaphore	cells_sem;
	wait_queue_head_t	cells_freeable_wq;
	struct work_struct	cells_manager;
	struct timer_list	cells_timer;
	atomic_t		cells_outstanding;
	seqlock_t		cells_lock;

	struct rw_semaphore	proc_cells_sem;
	spinlock_t		proc_cells_lock;
	struct list_head	proc_cells;

	/* Volume location database */
@@ -242,14 +243,26 @@ struct afs_net {

extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns

enum afs_cell_state {
	AFS_CELL_UNSET,
	AFS_CELL_ACTIVATING,
	AFS_CELL_ACTIVE,
	AFS_CELL_DEACTIVATING,
	AFS_CELL_INACTIVE,
	AFS_CELL_FAILED,
};

/*
 * AFS cell record
 */
struct afs_cell {
	atomic_t		usage;
	struct list_head	link;		/* main cell list link */
	struct afs_net		*net;		/* The network namespace */
	union {
		struct rcu_head	rcu;
		struct rb_node	net_node;	/* Node in net->cells */
	};
	struct afs_net		*net;
	struct key		*anonymous_key;	/* anonymous user key for this cell */
	struct work_struct	manager;	/* Manager for init/deinit/dns */
	struct list_head	proc_link;	/* /proc cell list link */
#ifdef CONFIG_AFS_FSCACHE
	struct fscache_cookie	*cache;		/* caching cookie */
@@ -262,12 +275,26 @@ struct afs_cell {
	/* volume location record management */
	struct rw_semaphore	vl_sem;		/* volume management serialisation semaphore */
	struct list_head	vl_list;	/* cell's active VL record list */
	time64_t		dns_expiry;	/* Time AFSDB/SRV record expires */
	time64_t		last_inactive;	/* Time of last drop of usage count */
	atomic_t		usage;
	unsigned long		flags;
#define AFS_CELL_FL_NOT_READY	0		/* The cell record is not ready for use */
#define AFS_CELL_FL_NO_GC	1		/* The cell was added manually, don't auto-gc */
#define AFS_CELL_FL_NOT_FOUND	2		/* Permanent DNS error */
#define AFS_CELL_FL_DNS_FAIL	3		/* Failed to access DNS */
	enum afs_cell_state	state;
	short			error;

	spinlock_t		vl_lock;	/* vl_list lock */

	/* VLDB server list. */
	seqlock_t		vl_addrs_lock;
	unsigned short		vl_naddrs;	/* number of VL servers in addr list */
	unsigned short		vl_curr_svix;	/* current server index */
	struct sockaddr_rxrpc	vl_addrs[AFS_CELL_MAX_ADDRS];	/* cell VL server addresses */

	char			name[0];	/* cell name - must go last */
	u8			name_len;	/* Length of name */
	char			name[64 + 1];	/* Cell name, case-flattened and NUL-padded */
};

/*
@@ -500,11 +527,14 @@ static inline struct afs_cell *afs_get_cell(struct afs_cell *cell)
		atomic_inc(&cell->usage);
	return cell;
}
extern int afs_cell_init(struct afs_net *, char *);
extern struct afs_cell *afs_cell_create(struct afs_net *, const char *, unsigned, char *, bool);
extern struct afs_cell *afs_cell_lookup(struct afs_net *, const char *, unsigned, bool);
extern struct afs_cell *afs_grab_cell(struct afs_cell *);

extern int afs_cell_init(struct afs_net *, const char *);
extern struct afs_cell *afs_lookup_cell_rcu(struct afs_net *, const char *, unsigned);
extern struct afs_cell *afs_lookup_cell(struct afs_net *, const char *, unsigned,
					const char *, bool);
extern void afs_put_cell(struct afs_net *, struct afs_cell *);
extern void afs_manage_cells(struct work_struct *);
extern void afs_cells_timer(struct timer_list *);
extern void __net_exit afs_cell_purge(struct afs_net *);

/*
+11 −5
Original line number Diff line number Diff line
@@ -46,12 +46,15 @@ static int __net_init afs_net_init(struct afs_net *net)

	INIT_WORK(&net->charge_preallocation_work, afs_charge_preallocation);
	mutex_init(&net->socket_mutex);
	INIT_LIST_HEAD(&net->cells);
	rwlock_init(&net->cells_lock);
	init_rwsem(&net->cells_sem);
	init_waitqueue_head(&net->cells_freeable_wq);
	init_rwsem(&net->proc_cells_sem);

	net->cells = RB_ROOT;
	seqlock_init(&net->cells_lock);
	INIT_WORK(&net->cells_manager, afs_manage_cells);
	timer_setup(&net->cells_timer, afs_cells_timer, 0);

	spin_lock_init(&net->proc_cells_lock);
	INIT_LIST_HEAD(&net->proc_cells);

	INIT_LIST_HEAD(&net->vl_updates);
	INIT_LIST_HEAD(&net->vl_graveyard);
	INIT_DELAYED_WORK(&net->vl_reaper, afs_vlocation_reaper);
@@ -83,11 +86,14 @@ static int __net_init afs_net_init(struct afs_net *net)
	return 0;

error_open_socket:
	net->live = false;
	afs_vlocation_purge(net);
	afs_cell_purge(net);
error_cell_init:
	net->live = false;
	afs_proc_cleanup(net);
error_proc:
	net->live = false;
	return ret;
}

+6 −9
Original line number Diff line number Diff line
@@ -186,7 +186,7 @@ static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
{
	struct afs_net *net = afs_seq2net(m);

	down_read(&net->proc_cells_sem);
	rcu_read_lock();
	return seq_list_start_head(&net->proc_cells, *_pos);
}

@@ -205,9 +205,7 @@ static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
 */
static void afs_proc_cells_stop(struct seq_file *m, void *v)
{
	struct afs_net *net = afs_seq2net(m);

	up_read(&net->proc_cells_sem);
	rcu_read_unlock();
}

/*
@@ -225,8 +223,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v)
	}

	/* display one cell per line on subsequent lines */
	seq_printf(m, "%3d %s\n",
		   atomic_read(&cell->usage), cell->name);
	seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
	return 0;
}

@@ -279,13 +276,13 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
	if (strcmp(kbuf, "add") == 0) {
		struct afs_cell *cell;

		cell = afs_cell_create(net, name, strlen(name), args, false);
		cell = afs_lookup_cell(net, name, strlen(name), args, true);
		if (IS_ERR(cell)) {
			ret = PTR_ERR(cell);
			goto done;
		}

		afs_put_cell(net, cell);
		set_bit(AFS_CELL_FL_NO_GC, &cell->flags);
		printk("kAFS: Added new cell '%s'\n", name);
	} else {
		goto inval;
@@ -354,7 +351,7 @@ int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell)
{
	struct proc_dir_entry *dir;

	_enter("%p{%s}", cell, cell->name);
	_enter("%p{%s},%p", cell, cell->name, net->proc_afs);

	dir = proc_mkdir(cell->name, net->proc_afs);
	if (!dir)
+7 −5
Original line number Diff line number Diff line
@@ -200,10 +200,11 @@ static int afs_parse_options(struct afs_mount_params *params,
		token = match_token(p, afs_options_list, args);
		switch (token) {
		case afs_opt_cell:
			cell = afs_cell_lookup(params->net,
			rcu_read_lock();
			cell = afs_lookup_cell_rcu(params->net,
						   args[0].from,
					       args[0].to - args[0].from,
					       false);
						   args[0].to - args[0].from);
			rcu_read_unlock();
			if (IS_ERR(cell))
				return PTR_ERR(cell);
			afs_put_cell(params->net, params->cell);
@@ -308,7 +309,8 @@ static int afs_parse_device_name(struct afs_mount_params *params,

	/* lookup the cell record */
	if (cellname || !params->cell) {
		cell = afs_cell_lookup(params->net, cellname, cellnamesz, true);
		cell = afs_lookup_cell(params->net, cellname, cellnamesz,
				       NULL, false);
		if (IS_ERR(cell)) {
			printk(KERN_ERR "kAFS: unable to lookup cell '%*.*s'\n",
			       cellnamesz, cellnamesz, cellname ?: "");
Loading