Skip to content

Commit 2d900ef

Browse files
Davidlohr Buesobrauner
authored andcommitted
mm/migrate: fix sleep in atomic for large folios and buffer heads
The large folio + buffer head noref migration scenarios are being naughty and blocking while holding a spinlock. As a consequence of the pagecache lookup path taking the folio lock this serializes against migration paths, so they can wait for each other. For the private_lock atomic case, a new BH_Migrate flag is introduced which enables the lookup to bail. This allows the critical region of the private_lock on the migration path to be reduced to the way it was before ebdf4de ("mm: migrate: fix reference check race between __find_get_block() and migration"), that is covering the count checks. The scope is always noref migration. Reported-by: kernel test robot <oliver.sang@intel.com> Reported-by: syzbot+f3c6fda1297c748a7076@syzkaller.appspotmail.com Closes: https://lore.kernel.org/oe-lkp/202503101536.27099c77-lkp@intel.com Fixes: 3c20917 ("block/bdev: enable large folio support for large logical block sizes") Reviewed-by: Jan Kara <jack@suse.cz> Co-developed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Davidlohr Bueso <dave@stgolabs.net> Link: https://kdevops.org/ext4/v6.15-rc2.html # [0] Link: https://lore.kernel.org/all/aAAEvcrmREWa1SKF@bombadil.infradead.org/ # [1] Link: https://lore.kernel.org/20250418015921.132400-8-dave@stgolabs.net Tested-by: kdevops@lists.linux.dev # [0] [1] Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 6e8f57f commit 2d900ef

File tree

4 files changed

+19
-5
lines changed

4 files changed

+19
-5
lines changed

fs/buffer.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ __find_get_block_slow(struct block_device *bdev, sector_t block, bool atomic)
207207
head = folio_buffers(folio);
208208
if (!head)
209209
goto out_unlock;
210+
/*
211+
* Upon a noref migration, the folio lock serializes here;
212+
* otherwise bail.
213+
*/
214+
if (test_bit_acquire(BH_Migrate, &head->b_state)) {
215+
WARN_ON(!atomic);
216+
goto out_unlock;
217+
}
218+
210219
bh = head;
211220
do {
212221
if (!buffer_mapped(bh))
@@ -1390,7 +1399,8 @@ lookup_bh_lru(struct block_device *bdev, sector_t block, unsigned size)
13901399
/*
13911400
* Perform a pagecache lookup for the matching buffer. If it's there, refresh
13921401
* it in the LRU and mark it as accessed. If it is not present then return
1393-
* NULL
1402+
* NULL. Atomic context callers may also return NULL if the buffer is being
1403+
* migrated; similarly the page is not marked accessed either.
13941404
*/
13951405
static struct buffer_head *
13961406
find_get_block_common(struct block_device *bdev, sector_t block,

fs/ext4/ialloc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,8 @@ static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino)
691691
if (!bh || !buffer_uptodate(bh))
692692
/*
693693
* If the block is not in the buffer cache, then it
694-
* must have been written out.
694+
* must have been written out, or, most unlikely, is
695+
* being migrated - false failure should be OK here.
695696
*/
696697
goto out;
697698

include/linux/buffer_head.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum bh_state_bits {
3434
BH_Meta, /* Buffer contains metadata */
3535
BH_Prio, /* Buffer should be submitted with REQ_PRIO */
3636
BH_Defer_Completion, /* Defer AIO completion to workqueue */
37+
BH_Migrate, /* Buffer is being migrated (norefs) */
3738

3839
BH_PrivateStart,/* not a state bit, but the first bit available
3940
* for private allocation by other entities

mm/migrate.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -845,9 +845,11 @@ static int __buffer_migrate_folio(struct address_space *mapping,
845845
return -EAGAIN;
846846

847847
if (check_refs) {
848-
bool busy;
848+
bool busy, migrating;
849849
bool invalidated = false;
850850

851+
migrating = test_and_set_bit_lock(BH_Migrate, &head->b_state);
852+
VM_WARN_ON_ONCE(migrating);
851853
recheck_buffers:
852854
busy = false;
853855
spin_lock(&mapping->i_private_lock);
@@ -859,12 +861,12 @@ static int __buffer_migrate_folio(struct address_space *mapping,
859861
}
860862
bh = bh->b_this_page;
861863
} while (bh != head);
864+
spin_unlock(&mapping->i_private_lock);
862865
if (busy) {
863866
if (invalidated) {
864867
rc = -EAGAIN;
865868
goto unlock_buffers;
866869
}
867-
spin_unlock(&mapping->i_private_lock);
868870
invalidate_bh_lrus();
869871
invalidated = true;
870872
goto recheck_buffers;
@@ -883,7 +885,7 @@ static int __buffer_migrate_folio(struct address_space *mapping,
883885

884886
unlock_buffers:
885887
if (check_refs)
886-
spin_unlock(&mapping->i_private_lock);
888+
clear_bit_unlock(BH_Migrate, &head->b_state);
887889
bh = head;
888890
do {
889891
unlock_buffer(bh);

0 commit comments

Comments
 (0)