Skip to content

Commit c69439a

Browse files
Christoph Hellwigcmaiolino
authored andcommitted
xfs: fix a buffer lookup against removal race
When a buffer is freed either by LRU eviction or because it is unset, the lockref is marked as dead instantly, which prevents the buffer from being used after finding it in the buffer hash in xfs_buf_lookup and xfs_buf_find_insert. But the latter will then not add the new buffer to the hash because it already found an existing buffer. Fix this using in two places: Remove the buffer from the hash before marking the lockref dead so that that no buffer with a dead lockref can be found in the hash, but if we find one in xfs_buf_find_insert due to store reordering, handle this case correctly instead of returning an unhashed buffer. Fixes: 67fe430 ("xfs: don't keep a reference for buffers on the LRU") Reported-by: Andrey Albershteyn <aalbersh@redhat.com> Reported-by: Carlos Maiolino <cem@kernel.org> Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org> Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com> Signed-off-by: Carlos Maiolino <cem@kernel.org>
1 parent 509fdeb commit c69439a

1 file changed

Lines changed: 24 additions & 10 deletions

File tree

fs/xfs/xfs_buf.c

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ xfs_buf_find_insert(
472472
/* The new buffer keeps the perag reference until it is freed. */
473473
new_bp->b_pag = pag;
474474

475+
retry:
475476
rcu_read_lock();
476477
bp = rhashtable_lookup_get_insert_fast(&btp->bt_hash,
477478
&new_bp->b_rhash_head, xfs_buf_hash_params);
@@ -480,8 +481,16 @@ xfs_buf_find_insert(
480481
error = PTR_ERR(bp);
481482
goto out_free_buf;
482483
}
483-
if (bp && lockref_get_not_dead(&bp->b_lockref)) {
484-
/* found an existing buffer */
484+
if (bp) {
485+
/*
486+
* If there is an existing buffer with a dead lockref, retry
487+
* until the new buffer is added, or a usable buffer is found.
488+
*/
489+
if (!lockref_get_not_dead(&bp->b_lockref)) {
490+
rcu_read_unlock();
491+
cpu_relax();
492+
goto retry;
493+
}
485494
rcu_read_unlock();
486495
error = xfs_buf_find_lock(bp, flags);
487496
if (error)
@@ -820,15 +829,20 @@ xfs_buf_destroy(
820829
ASSERT(__lockref_is_dead(&bp->b_lockref));
821830
ASSERT(!(bp->b_flags & _XBF_DELWRI_Q));
822831

832+
if (bp->b_pag)
833+
xfs_perag_put(bp->b_pag);
834+
xfs_buf_free(bp);
835+
}
836+
837+
static inline void
838+
xfs_buf_kill(
839+
struct xfs_buf *bp)
840+
{
841+
lockref_mark_dead(&bp->b_lockref);
823842
if (!xfs_buf_is_uncached(bp)) {
824843
rhashtable_remove_fast(&bp->b_target->bt_hash,
825844
&bp->b_rhash_head, xfs_buf_hash_params);
826-
827-
if (bp->b_pag)
828-
xfs_perag_put(bp->b_pag);
829845
}
830-
831-
xfs_buf_free(bp);
832846
}
833847

834848
/*
@@ -851,7 +865,7 @@ xfs_buf_rele(
851865
return;
852866

853867
kill:
854-
lockref_mark_dead(&bp->b_lockref);
868+
xfs_buf_kill(bp);
855869
list_lru_del_obj(&bp->b_target->bt_lru, &bp->b_lru);
856870
spin_unlock(&bp->b_lockref.lock);
857871

@@ -1433,7 +1447,7 @@ xfs_buftarg_drain_rele(
14331447
return LRU_SKIP;
14341448
}
14351449

1436-
lockref_mark_dead(&bp->b_lockref);
1450+
xfs_buf_kill(bp);
14371451
list_lru_isolate_move(lru, item, dispose);
14381452
spin_unlock(&bp->b_lockref.lock);
14391453
return LRU_REMOVED;
@@ -1545,7 +1559,7 @@ xfs_buftarg_isolate(
15451559
return LRU_ROTATE;
15461560
}
15471561

1548-
lockref_mark_dead(&bp->b_lockref);
1562+
xfs_buf_kill(bp);
15491563
list_lru_isolate_move(lru, item, dispose);
15501564
spin_unlock(&bp->b_lockref.lock);
15511565
return LRU_REMOVED;

0 commit comments

Comments
 (0)