afs: Fix cell purging with aliases
authorDavid Howells <dhowells@redhat.com>
Thu, 15 Oct 2020 10:05:01 +0000 (11:05 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 16 Oct 2020 13:38:26 +0000 (14:38 +0100)
When the afs module is removed, one of the things that has to be done is to
purge the cell database.  afs_cell_purge() cancels the management timer and
then starts the cell manager work item to do the purging.  This does a
single run through and then assumes that all cells are now purged - but
this is no longer the case.

With the introduction of alias detection, a later cell in the database can
now be holding an active count on an earlier cell (cell->alias_of).  The
purge scan passes by the earlier cell first, but this can't be got rid of
until it has discarded the alias.  Ordinarily, afs_unuse_cell() would
handle this by setting the management timer to trigger another pass - but
afs_set_cell_timer() doesn't do anything if the namespace is being removed
(net->live == false).  rmmod then hangs in the wait on cells_outstanding in
afs_cell_purge().

Fix this by making afs_set_cell_timer() directly queue the cell manager if
net->live is false.  This causes additional management passes.

Queueing the cell manager increments cells_outstanding to make sure the
wait won't complete until all cells are destroyed.

Fixes: 8a070a964877 ("afs: Detect cell aliases 1 - Cells with root volumes")
Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/cell.c

index c906000b0ff84840092b267b44088e6101988e35..1944be78e9b0d849454b6ff45dde0396afd70fc4 100644 (file)
@@ -19,6 +19,7 @@ static unsigned __read_mostly afs_cell_gc_delay = 10;
 static unsigned __read_mostly afs_cell_min_ttl = 10 * 60;
 static unsigned __read_mostly afs_cell_max_ttl = 24 * 60 * 60;
 
+static void afs_queue_cell_manager(struct afs_net *);
 static void afs_manage_cell_work(struct work_struct *);
 
 static void afs_dec_cells_outstanding(struct afs_net *net)
@@ -37,6 +38,8 @@ static void afs_set_cell_timer(struct afs_net *net, time64_t delay)
                atomic_inc(&net->cells_outstanding);
                if (timer_reduce(&net->cells_timer, jiffies + delay * HZ))
                        afs_dec_cells_outstanding(net);
+       } else {
+               afs_queue_cell_manager(net);
        }
 }