From: David Howells <dhowells@xxxxxxxxxx> [ Upstream commit 1d0e850a49a5b56f8f3cb51e74a11e2fedb96be6 ] Fix cell removal by inserting a more final state than AFS_CELL_FAILED that indicates that the cell has been unpublished in case the manager is already requeued and will go through again. The new AFS_CELL_REMOVED state will just immediately leave the manager function. Going through a second time in the AFS_CELL_FAILED state will cause it to try to remove the cell again, potentially leading to the proc list being removed. Fixes: 989782dcdc91 ("afs: Overhaul cell database management") Reported-by: syzbot+b994ecf2b023f14832c1@xxxxxxxxxxxxxxxxxxxxxxxxx Reported-by: syzbot+0e0db88e1eb44a91ae8d@xxxxxxxxxxxxxxxxxxxxxxxxx Reported-by: syzbot+2d0585e5efcd43d113c2@xxxxxxxxxxxxxxxxxxxxxxxxx Reported-by: syzbot+1ecc2f9d3387f1d79d42@xxxxxxxxxxxxxxxxxxxxxxxxx Reported-by: syzbot+18d51774588492bf3f69@xxxxxxxxxxxxxxxxxxxxxxxxx Reported-by: syzbot+a5e4946b04d6ca8fa5f3@xxxxxxxxxxxxxxxxxxxxxxxxx Suggested-by: Hillf Danton <hdanton@xxxxxxxx> Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: Hillf Danton <hdanton@xxxxxxxx> Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> --- fs/afs/cell.c | 16 ++++++++++------ fs/afs/internal.h | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/afs/cell.c b/fs/afs/cell.c index 1944be78e9b0d..bc7ed46aaca9f 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -291,11 +291,11 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, wait_var_event(&cell->state, ({ state = smp_load_acquire(&cell->state); /* vs error */ - state == AFS_CELL_ACTIVE || state == AFS_CELL_FAILED; + state == AFS_CELL_ACTIVE || state == AFS_CELL_REMOVED; })); /* Check the state obtained from the wait check. */ - if (state == AFS_CELL_FAILED) { + if (state == AFS_CELL_REMOVED) { ret = cell->error; goto error; } @@ -700,7 +700,6 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell) static void afs_manage_cell(struct afs_cell *cell) { struct afs_net *net = cell->net; - bool deleted; int ret, active; _enter("%s", cell->name); @@ -712,13 +711,15 @@ static void afs_manage_cell(struct afs_cell *cell) case AFS_CELL_FAILED: down_write(&net->cells_lock); active = 1; - deleted = atomic_try_cmpxchg_relaxed(&cell->active, &active, 0); - if (deleted) { + if (atomic_try_cmpxchg_relaxed(&cell->active, &active, 0)) { rb_erase(&cell->net_node, &net->cells); + smp_store_release(&cell->state, AFS_CELL_REMOVED); } up_write(&net->cells_lock); - if (deleted) + if (cell->state == AFS_CELL_REMOVED) { + wake_up_var(&cell->state); goto final_destruction; + } if (cell->state == AFS_CELL_FAILED) goto done; smp_store_release(&cell->state, AFS_CELL_UNSET); @@ -760,6 +761,9 @@ static void afs_manage_cell(struct afs_cell *cell) wake_up_var(&cell->state); goto again; + case AFS_CELL_REMOVED: + goto done; + default: break; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 522597b401fec..7689f4535ef9c 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -326,6 +326,7 @@ enum afs_cell_state { AFS_CELL_DEACTIVATING, AFS_CELL_INACTIVE, AFS_CELL_FAILED, + AFS_CELL_REMOVED, }; /* -- 2.25.1