forked from Minki/linux
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
988
fs/afs/cell.c
988
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;
|
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 *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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 ?: "");
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user