Skip to content

Commit 7ffe3de

Browse files
Davidlohr Buesobrauner
authored andcommitted
fs/buffer: split locking for pagecache lookups
Callers of __find_get_block() may or may not allow for blocking semantics, and is currently assumed that it will not. Layout two paths based on this. The the private_lock scheme will continued to be used for atomic contexts. Otherwise take the folio lock instead, which protects the buffers, such as vs migration and try_to_free_buffers(). Per the "hack idea", the latter can alleviate contention on the private_lock for bdev mappings. For reasons of determinism and avoid making bugs hard to reproduce, the trylocking is not attempted. No change in semantics. All lookup users still take the spinlock. Reviewed-by: Jan Kara <jack@suse.cz> 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-2-dave@stgolabs.net Tested-by: kdevops@lists.linux.dev Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 559a0d7 commit 7ffe3de

File tree

1 file changed

+25
-16
lines changed

1 file changed

+25
-16
lines changed

fs/buffer.c

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,8 @@ void end_buffer_write_sync(struct buffer_head *bh, int uptodate)
176176
}
177177
EXPORT_SYMBOL(end_buffer_write_sync);
178178

179-
/*
180-
* Various filesystems appear to want __find_get_block to be non-blocking.
181-
* But it's the page lock which protects the buffers. To get around this,
182-
* we get exclusion from try_to_free_buffers with the blockdev mapping's
183-
* i_private_lock.
184-
*
185-
* Hack idea: for the blockdev mapping, i_private_lock contention
186-
* may be quite high. This code could TryLock the page, and if that
187-
* succeeds, there is no need to take i_private_lock.
188-
*/
189179
static struct buffer_head *
190-
__find_get_block_slow(struct block_device *bdev, sector_t block)
180+
__find_get_block_slow(struct block_device *bdev, sector_t block, bool atomic)
191181
{
192182
struct address_space *bd_mapping = bdev->bd_mapping;
193183
const int blkbits = bd_mapping->host->i_blkbits;
@@ -204,7 +194,16 @@ __find_get_block_slow(struct block_device *bdev, sector_t block)
204194
if (IS_ERR(folio))
205195
goto out;
206196

207-
spin_lock(&bd_mapping->i_private_lock);
197+
/*
198+
* Folio lock protects the buffers. Callers that cannot block
199+
* will fallback to serializing vs try_to_free_buffers() via
200+
* the i_private_lock.
201+
*/
202+
if (atomic)
203+
spin_lock(&bd_mapping->i_private_lock);
204+
else
205+
folio_lock(folio);
206+
208207
head = folio_buffers(folio);
209208
if (!head)
210209
goto out_unlock;
@@ -236,7 +235,10 @@ __find_get_block_slow(struct block_device *bdev, sector_t block)
236235
1 << blkbits);
237236
}
238237
out_unlock:
239-
spin_unlock(&bd_mapping->i_private_lock);
238+
if (atomic)
239+
spin_unlock(&bd_mapping->i_private_lock);
240+
else
241+
folio_unlock(folio);
240242
folio_put(folio);
241243
out:
242244
return ret;
@@ -1388,21 +1390,28 @@ lookup_bh_lru(struct block_device *bdev, sector_t block, unsigned size)
13881390
* it in the LRU and mark it as accessed. If it is not present then return
13891391
* NULL
13901392
*/
1391-
struct buffer_head *
1392-
__find_get_block(struct block_device *bdev, sector_t block, unsigned size)
1393+
static struct buffer_head *
1394+
find_get_block_common(struct block_device *bdev, sector_t block,
1395+
unsigned size, bool atomic)
13931396
{
13941397
struct buffer_head *bh = lookup_bh_lru(bdev, block, size);
13951398

13961399
if (bh == NULL) {
13971400
/* __find_get_block_slow will mark the page accessed */
1398-
bh = __find_get_block_slow(bdev, block);
1401+
bh = __find_get_block_slow(bdev, block, atomic);
13991402
if (bh)
14001403
bh_lru_install(bh);
14011404
} else
14021405
touch_buffer(bh);
14031406

14041407
return bh;
14051408
}
1409+
1410+
struct buffer_head *
1411+
__find_get_block(struct block_device *bdev, sector_t block, unsigned size)
1412+
{
1413+
return find_get_block_common(bdev, block, size, true);
1414+
}
14061415
EXPORT_SYMBOL(__find_get_block);
14071416

14081417
/**

0 commit comments

Comments
 (0)