Skip to content

Commit

Permalink
afs: Fix server reaping
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Howells committed Nov 13, 2017
1 parent e3b2ffe commit 59fa1c4
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 10 deletions.
5 changes: 4 additions & 1 deletion fs/afs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,9 @@ struct afs_net {
rwlock_t servers_lock;
struct list_head server_graveyard; /* Inactive server LRU list */
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 */
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
Expand Down Expand Up @@ -700,6 +702,7 @@ do { \
atomic_inc(&(S)->usage); \
} while(0)

extern void afs_server_timer(struct timer_list *);
extern struct afs_server *afs_lookup_server(struct afs_cell *,
const struct in_addr *);
extern struct afs_server *afs_find_server(struct afs_net *,
Expand Down
3 changes: 2 additions & 1 deletion fs/afs/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ static int __net_init afs_net_init(struct afs_net *net)
rwlock_init(&net->servers_lock);
INIT_LIST_HEAD(&net->server_graveyard);
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 */
ret = afs_proc_init(net);
Expand Down
59 changes: 51 additions & 8 deletions fs/afs/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,25 @@

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
*/
Expand Down Expand Up @@ -81,6 +100,7 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell,

memcpy(&server->addr, addr, sizeof(struct in_addr));
server->addr.s_addr = addr->s_addr;
afs_inc_servers_outstanding(cell->net);
_leave(" = %p{%d}", server, atomic_read(&server->usage));
} else {
_leave(" = NULL [nomem]");
Expand Down Expand Up @@ -159,6 +179,7 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell,
server_in_two_cells:
write_unlock(&cell->servers_lock);
kfree(candidate);
afs_dec_servers_outstanding(cell->net);
printk(KERN_NOTICE "kAFS: Server %pI4 appears to be in two cells\n",
addr);
_leave(" = -EEXIST");
Expand Down Expand Up @@ -208,6 +229,18 @@ struct afs_server *afs_find_server(struct afs_net *net,
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
* - removes from the cell list
Expand Down Expand Up @@ -236,8 +269,7 @@ void afs_put_server(struct afs_server *server)
if (atomic_read(&server->usage) == 0) {
list_move_tail(&server->grave, &net->server_graveyard);
server->time_of_death = ktime_get_real_seconds();
queue_delayed_work(afs_wq, &net->server_reaper,
net->live ? afs_server_timeout * HZ : 0);
afs_set_server_timer(net, afs_server_timeout);
}
spin_unlock(&net->server_graveyard_lock);
_leave(" [dead]");
Expand All @@ -246,7 +278,7 @@ void afs_put_server(struct afs_server *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);

Expand All @@ -260,6 +292,7 @@ static void afs_destroy_server(struct afs_server *server)

afs_put_cell(server->cell);
kfree(server);
afs_dec_servers_outstanding(net);
}

/*
Expand All @@ -269,7 +302,7 @@ void afs_reap_server(struct work_struct *work)
{
LIST_HEAD(corpses);
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;
time64_t now;

Expand All @@ -284,8 +317,8 @@ void afs_reap_server(struct work_struct *work)
if (net->live) {
expiry = server->time_of_death + afs_server_timeout;
if (expiry > now) {
delay = (expiry - now) * HZ;
mod_delayed_work(afs_wq, &net->server_reaper, delay);
delay = (expiry - now);
afs_set_server_timer(net, delay);
break;
}
}
Expand All @@ -309,8 +342,10 @@ void afs_reap_server(struct work_struct *work)
while (!list_empty(&corpses)) {
server = list_entry(corpses.next, struct afs_server, grave);
list_del(&server->grave);
afs_destroy_server(server);
afs_destroy_server(net, server);
}

afs_dec_servers_outstanding(net);
}

/*
Expand All @@ -319,5 +354,13 @@ void afs_reap_server(struct work_struct *work)
*/
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);
}

0 comments on commit 59fa1c4

Please sign in to comment.