mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: David Howells <dhowells@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									be080a6f43
								
							
						
					
					
						commit
						989782dcdc
					
				
					 6 changed files with 740 additions and 353 deletions
				
			
		
							
								
								
									
										964
									
								
								fs/afs/cell.c
									
									
									
									
									
								
							
							
						
						
									
										964
									
								
								fs/afs/cell.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -494,17 +521,20 @@ static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest
 | 
			
		|||
/*
 | 
			
		||||
 * cell.c
 | 
			
		||||
 */
 | 
			
		||||
static inline struct afs_cell *afs_get_cell(struct afs_cell *cell)
 | 
			
		||||
 static inline struct afs_cell *afs_get_cell(struct afs_cell *cell)
 | 
			
		||||
{
 | 
			
		||||
	if (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 *);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 ?: "");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ static int afs_xattr_get_cell(const struct xattr_handler *handler,
 | 
			
		|||
	struct afs_cell *cell = vnode->volume->cell;
 | 
			
		||||
	size_t namelen;
 | 
			
		||||
 | 
			
		||||
	namelen = strlen(cell->name);
 | 
			
		||||
	namelen = cell->name_len;
 | 
			
		||||
	if (size == 0)
 | 
			
		||||
		return namelen;
 | 
			
		||||
	if (namelen > size)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue