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:
David Howells 2017-11-02 15:27:50 +00:00
parent be080a6f43
commit 989782dcdc
6 changed files with 740 additions and 353 deletions

File diff suppressed because it is too large Load Diff

View File

@ -207,13 +207,14 @@ struct afs_net {
atomic_t nr_superblocks; atomic_t nr_superblocks;
/* Cell database */ /* Cell database */
struct list_head cells; struct rb_root cells;
struct afs_cell *ws_cell; struct afs_cell *ws_cell;
rwlock_t cells_lock; struct work_struct cells_manager;
struct rw_semaphore cells_sem; struct timer_list cells_timer;
wait_queue_head_t cells_freeable_wq; atomic_t cells_outstanding;
seqlock_t cells_lock;
struct rw_semaphore proc_cells_sem; spinlock_t proc_cells_lock;
struct list_head proc_cells; struct list_head proc_cells;
/* Volume location database */ /* 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 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 * AFS cell record
*/ */
struct afs_cell { struct afs_cell {
atomic_t usage; union {
struct list_head link; /* main cell list link */ struct rcu_head rcu;
struct afs_net *net; /* The network namespace */ struct rb_node net_node; /* Node in net->cells */
};
struct afs_net *net;
struct key *anonymous_key; /* anonymous user key for this cell */ 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 */ struct list_head proc_link; /* /proc cell list link */
#ifdef CONFIG_AFS_FSCACHE #ifdef CONFIG_AFS_FSCACHE
struct fscache_cookie *cache; /* caching cookie */ struct fscache_cookie *cache; /* caching cookie */
@ -262,12 +275,26 @@ struct afs_cell {
/* volume location record management */ /* volume location record management */
struct rw_semaphore vl_sem; /* volume management serialisation semaphore */ struct rw_semaphore vl_sem; /* volume management serialisation semaphore */
struct list_head vl_list; /* cell's active VL record list */ 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 */ 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_naddrs; /* number of VL servers in addr list */
unsigned short vl_curr_svix; /* current server index */ unsigned short vl_curr_svix; /* current server index */
struct sockaddr_rxrpc vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */ struct sockaddr_rxrpc vl_addrs[AFS_CELL_MAX_ADDRS]; /* cell VL server addresses */
u8 name_len; /* Length of name */
char name[0]; /* cell name - must go last */ 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 * 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) if (cell)
atomic_inc(&cell->usage); atomic_inc(&cell->usage);
return cell; 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 int afs_cell_init(struct afs_net *, const char *);
extern struct afs_cell *afs_cell_lookup(struct afs_net *, const char *, unsigned, bool); extern struct afs_cell *afs_lookup_cell_rcu(struct afs_net *, const char *, unsigned);
extern struct afs_cell *afs_grab_cell(struct afs_cell *); 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_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 *); extern void __net_exit afs_cell_purge(struct afs_net *);
/* /*

View File

@ -46,12 +46,15 @@ static int __net_init afs_net_init(struct afs_net *net)
INIT_WORK(&net->charge_preallocation_work, afs_charge_preallocation); INIT_WORK(&net->charge_preallocation_work, afs_charge_preallocation);
mutex_init(&net->socket_mutex); mutex_init(&net->socket_mutex);
INIT_LIST_HEAD(&net->cells);
rwlock_init(&net->cells_lock); net->cells = RB_ROOT;
init_rwsem(&net->cells_sem); seqlock_init(&net->cells_lock);
init_waitqueue_head(&net->cells_freeable_wq); INIT_WORK(&net->cells_manager, afs_manage_cells);
init_rwsem(&net->proc_cells_sem); 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->proc_cells);
INIT_LIST_HEAD(&net->vl_updates); INIT_LIST_HEAD(&net->vl_updates);
INIT_LIST_HEAD(&net->vl_graveyard); INIT_LIST_HEAD(&net->vl_graveyard);
INIT_DELAYED_WORK(&net->vl_reaper, afs_vlocation_reaper); 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; return 0;
error_open_socket: error_open_socket:
net->live = false;
afs_vlocation_purge(net); afs_vlocation_purge(net);
afs_cell_purge(net); afs_cell_purge(net);
error_cell_init: error_cell_init:
net->live = false;
afs_proc_cleanup(net); afs_proc_cleanup(net);
error_proc: error_proc:
net->live = false;
return ret; return ret;
} }

View File

@ -186,7 +186,7 @@ static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
{ {
struct afs_net *net = afs_seq2net(m); 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); 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) static void afs_proc_cells_stop(struct seq_file *m, void *v)
{ {
struct afs_net *net = afs_seq2net(m); rcu_read_unlock();
up_read(&net->proc_cells_sem);
} }
/* /*
@ -225,8 +223,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v)
} }
/* display one cell per line on subsequent lines */ /* display one cell per line on subsequent lines */
seq_printf(m, "%3d %s\n", seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
atomic_read(&cell->usage), cell->name);
return 0; 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) { if (strcmp(kbuf, "add") == 0) {
struct afs_cell *cell; 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)) { if (IS_ERR(cell)) {
ret = PTR_ERR(cell); ret = PTR_ERR(cell);
goto done; goto done;
} }
afs_put_cell(net, cell); set_bit(AFS_CELL_FL_NO_GC, &cell->flags);
printk("kAFS: Added new cell '%s'\n", name); printk("kAFS: Added new cell '%s'\n", name);
} else { } else {
goto inval; goto inval;
@ -354,7 +351,7 @@ int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell)
{ {
struct proc_dir_entry *dir; 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); dir = proc_mkdir(cell->name, net->proc_afs);
if (!dir) if (!dir)

View File

@ -200,10 +200,11 @@ static int afs_parse_options(struct afs_mount_params *params,
token = match_token(p, afs_options_list, args); token = match_token(p, afs_options_list, args);
switch (token) { switch (token) {
case afs_opt_cell: case afs_opt_cell:
cell = afs_cell_lookup(params->net, rcu_read_lock();
args[0].from, cell = afs_lookup_cell_rcu(params->net,
args[0].to - args[0].from, args[0].from,
false); args[0].to - args[0].from);
rcu_read_unlock();
if (IS_ERR(cell)) if (IS_ERR(cell))
return PTR_ERR(cell); return PTR_ERR(cell);
afs_put_cell(params->net, params->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 */ /* lookup the cell record */
if (cellname || !params->cell) { 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)) { if (IS_ERR(cell)) {
printk(KERN_ERR "kAFS: unable to lookup cell '%*.*s'\n", printk(KERN_ERR "kAFS: unable to lookup cell '%*.*s'\n",
cellnamesz, cellnamesz, cellname ?: ""); cellnamesz, cellnamesz, cellname ?: "");

View File

@ -45,7 +45,7 @@ static int afs_xattr_get_cell(const struct xattr_handler *handler,
struct afs_cell *cell = vnode->volume->cell; struct afs_cell *cell = vnode->volume->cell;
size_t namelen; size_t namelen;
namelen = strlen(cell->name); namelen = cell->name_len;
if (size == 0) if (size == 0)
return namelen; return namelen;
if (namelen > size) if (namelen > size)