afs: Fix server reaping

Fix server reaping and make sure it's all done before we start trying to
purge cells, given that servers currently pin cells.

Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
David Howells 2017-11-02 15:27:45 +00:00
parent e3b2ffe0f0
commit 59fa1c4a9f
3 changed files with 57 additions and 10 deletions

View File

@ -238,7 +238,9 @@ struct afs_net {
rwlock_t servers_lock; rwlock_t servers_lock;
struct list_head server_graveyard; /* Inactive server LRU list */ struct list_head server_graveyard; /* Inactive server LRU list */
spinlock_t server_graveyard_lock; spinlock_t server_graveyard_lock;
struct delayed_work server_reaper; struct timer_list server_timer;
struct work_struct server_reaper;
atomic_t servers_outstanding;
/* Misc */ /* Misc */
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */ struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
@ -700,6 +702,7 @@ do { \
atomic_inc(&(S)->usage); \ atomic_inc(&(S)->usage); \
} while(0) } while(0)
extern void afs_server_timer(struct timer_list *);
extern struct afs_server *afs_lookup_server(struct afs_cell *, extern struct afs_server *afs_lookup_server(struct afs_cell *,
const struct in_addr *); const struct in_addr *);
extern struct afs_server *afs_find_server(struct afs_net *, extern struct afs_server *afs_find_server(struct afs_net *,

View File

@ -62,7 +62,8 @@ static int __net_init afs_net_init(struct afs_net *net)
rwlock_init(&net->servers_lock); rwlock_init(&net->servers_lock);
INIT_LIST_HEAD(&net->server_graveyard); INIT_LIST_HEAD(&net->server_graveyard);
spin_lock_init(&net->server_graveyard_lock); spin_lock_init(&net->server_graveyard_lock);
INIT_DELAYED_WORK(&net->server_reaper, afs_reap_server); INIT_WORK(&net->server_reaper, afs_reap_server);
timer_setup(&net->server_timer, afs_server_timer, 0);
/* Register the /proc stuff */ /* Register the /proc stuff */
ret = afs_proc_init(net); ret = afs_proc_init(net);

View File

@ -15,6 +15,25 @@
static unsigned afs_server_timeout = 10; /* server timeout in seconds */ static unsigned afs_server_timeout = 10; /* server timeout in seconds */
static void afs_inc_servers_outstanding(struct afs_net *net)
{
atomic_inc(&net->servers_outstanding);
}
static void afs_dec_servers_outstanding(struct afs_net *net)
{
if (atomic_dec_and_test(&net->servers_outstanding))
wake_up_atomic_t(&net->servers_outstanding);
}
void afs_server_timer(struct timer_list *timer)
{
struct afs_net *net = container_of(timer, struct afs_net, server_timer);
if (!queue_work(afs_wq, &net->server_reaper))
afs_dec_servers_outstanding(net);
}
/* /*
* install a server record in the master tree * install a server record in the master tree
*/ */
@ -81,6 +100,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell,
memcpy(&server->addr, addr, sizeof(struct in_addr)); memcpy(&server->addr, addr, sizeof(struct in_addr));
server->addr.s_addr = addr->s_addr; server->addr.s_addr = addr->s_addr;
afs_inc_servers_outstanding(cell->net);
_leave(" = %p{%d}", server, atomic_read(&server->usage)); _leave(" = %p{%d}", server, atomic_read(&server->usage));
} else { } else {
_leave(" = NULL [nomem]"); _leave(" = NULL [nomem]");
@ -159,6 +179,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell,
server_in_two_cells: server_in_two_cells:
write_unlock(&cell->servers_lock); write_unlock(&cell->servers_lock);
kfree(candidate); kfree(candidate);
afs_dec_servers_outstanding(cell->net);
printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n", printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
addr); addr);
_leave(" = -EEXIST"); _leave(" = -EEXIST");
@ -208,6 +229,18 @@ struct afs_server *afs_find_server(struct afs_net *net,
return server; return server;
} }
static void afs_set_server_timer(struct afs_net *net, time64_t delay)
{
afs_inc_servers_outstanding(net);
if (net->live) {
if (timer_reduce(&net->server_timer, jiffies + delay * HZ))
afs_dec_servers_outstanding(net);
} else {
if (!queue_work(afs_wq, &net->server_reaper))
afs_dec_servers_outstanding(net);
}
}
/* /*
* destroy a server record * destroy a server record
* - removes from the cell list * - removes from the cell list
@ -236,8 +269,7 @@ void afs_put_server(struct afs_server *server)
if (atomic_read(&server->usage) == 0) { if (atomic_read(&server->usage) == 0) {
list_move_tail(&server->grave, &net->server_graveyard); list_move_tail(&server->grave, &net->server_graveyard);
server->time_of_death = ktime_get_real_seconds(); server->time_of_death = ktime_get_real_seconds();
queue_delayed_work(afs_wq, &net->server_reaper, afs_set_server_timer(net, afs_server_timeout);
net->live ? afs_server_timeout * HZ : 0);
} }
spin_unlock(&net->server_graveyard_lock); spin_unlock(&net->server_graveyard_lock);
_leave(" [dead]"); _leave(" [dead]");
@ -246,7 +278,7 @@ void afs_put_server(struct afs_server *server)
/* /*
* destroy a dead server * destroy a dead server
*/ */
static void afs_destroy_server(struct afs_server *server) static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
{ {
_enter("%p", server); _enter("%p", server);
@ -260,6 +292,7 @@ static void afs_destroy_server(struct afs_server *server)
afs_put_cell(server->cell); afs_put_cell(server->cell);
kfree(server); kfree(server);
afs_dec_servers_outstanding(net);
} }
/* /*
@ -269,7 +302,7 @@ void afs_reap_server(struct work_struct *work)
{ {
LIST_HEAD(corpses); LIST_HEAD(corpses);
struct afs_server *server; struct afs_server *server;
struct afs_net *net = container_of(work, struct afs_net, server_reaper.work); struct afs_net *net = container_of(work, struct afs_net, server_reaper);
unsigned long delay, expiry; unsigned long delay, expiry;
time64_t now; time64_t now;
@ -284,8 +317,8 @@ void afs_reap_server(struct work_struct *work)
if (net->live) { if (net->live) {
expiry = server->time_of_death + afs_server_timeout; expiry = server->time_of_death + afs_server_timeout;
if (expiry > now) { if (expiry > now) {
delay = (expiry - now) * HZ; delay = (expiry - now);
mod_delayed_work(afs_wq, &net->server_reaper, delay); afs_set_server_timer(net, delay);
break; break;
} }
} }
@ -309,8 +342,10 @@ void afs_reap_server(struct work_struct *work)
while (!list_empty(&corpses)) { while (!list_empty(&corpses)) {
server = list_entry(corpses.next, struct afs_server, grave); server = list_entry(corpses.next, struct afs_server, grave);
list_del(&server->grave); list_del(&server->grave);
afs_destroy_server(server); afs_destroy_server(net, server);
} }
afs_dec_servers_outstanding(net);
} }
/* /*
@ -319,5 +354,13 @@ void afs_reap_server(struct work_struct *work)
*/ */
void __net_exit afs_purge_servers(struct afs_net *net) void __net_exit afs_purge_servers(struct afs_net *net)
{ {
mod_delayed_work(afs_wq, &net->server_reaper, 0); if (del_timer_sync(&net->server_timer))
atomic_dec(&net->servers_outstanding);
afs_inc_servers_outstanding(net);
if (!queue_work(afs_wq, &net->server_reaper))
afs_dec_servers_outstanding(net);
wait_on_atomic_t(&net->servers_outstanding, atomic_t_wait,
TASK_UNINTERRUPTIBLE);
} }