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
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 New Issue
Block a user