diff --git a/kmod/src/block.c b/kmod/src/block.c index 8249a3d6..eb2c3b2e 100644 --- a/kmod/src/block.c +++ b/kmod/src/block.c @@ -802,6 +802,44 @@ void scoutfs_block_writer_forget(struct super_block *sb, } } +/* + * Change a cached block's location. We're careful to only change its + * position in the rbtree. If we find another block existing at the new + * location then we remove it from the cache and forget it if it was + * dirty. + */ +void scoutfs_block_move(struct super_block *sb, + struct scoutfs_block_writer *wri, + struct scoutfs_block *bl, u64 blkno) +{ + DECLARE_BLOCK_INFO(sb, binf); + struct block_private *bp = BLOCK_PRIVATE(bl); + struct block_private *existing = NULL; + + spin_lock(&binf->lock); + + existing = walk_block_rbtree(&binf->root, blkno, NULL); + if (existing) { + /* only nesting of binf and wri locks */ + if (test_bit(BLOCK_BIT_DIRTY, &bp->bits)) { + spin_lock(&wri->lock); + if (test_bit(BLOCK_BIT_DIRTY, &bp->bits)) + block_forget(sb, wri, bp); + spin_unlock(&wri->lock); + } + block_remove(sb, existing); + } + + rb_erase(&bp->node, &binf->root); + RB_CLEAR_NODE(&bp->node); + bp->bl.blkno = blkno; + walk_block_rbtree(&binf->root, blkno, bp); + + TRACE_BLOCK(move, bp); + + spin_unlock(&binf->lock); +} + /* * The caller has ensured that no more dirtying will take place. This * helps the caller avoid doing a bunch of work before calling into the diff --git a/kmod/src/block.h b/kmod/src/block.h index abcdd440..8a405f11 100644 --- a/kmod/src/block.h +++ b/kmod/src/block.h @@ -44,6 +44,9 @@ void scoutfs_block_writer_forget_all(struct super_block *sb, void scoutfs_block_writer_forget(struct super_block *sb, struct scoutfs_block_writer *wri, struct scoutfs_block *bl); +void scoutfs_block_move(struct super_block *sb, + struct scoutfs_block_writer *wri, + struct scoutfs_block *bl, u64 blkno); bool scoutfs_block_writer_has_dirty(struct super_block *sb, struct scoutfs_block_writer *wri); u64 scoutfs_block_writer_dirty_bytes(struct super_block *sb, diff --git a/kmod/src/scoutfs_trace.h b/kmod/src/scoutfs_trace.h index 5f8db8a3..15fc7daa 100644 --- a/kmod/src/scoutfs_trace.h +++ b/kmod/src/scoutfs_trace.h @@ -2191,6 +2191,11 @@ DEFINE_EVENT(scoutfs_block_class, scoutfs_block_invalidate, int refcount, int io_count, unsigned long bits, u64 lru_moved), TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved) ); +DEFINE_EVENT(scoutfs_block_class, scoutfs_block_move, + TP_PROTO(struct super_block *sb, void *bp, u64 blkno, + int refcount, int io_count, unsigned long bits, u64 lru_moved), + TP_ARGS(sb, bp, blkno, refcount, io_count, bits, lru_moved) +); DEFINE_EVENT(scoutfs_block_class, scoutfs_block_mark_dirty, TP_PROTO(struct super_block *sb, void *bp, u64 blkno, int refcount, int io_count, unsigned long bits, u64 lru_moved),