Skip to content

Commit 509f006

Browse files
Hugh Dickinsbrauner
authored andcommitted
shmem: fix quota lock nesting in huge hole handling
i_pages lock nests inside i_lock, but shmem_charge() and shmem_uncharge() were being called from THP splitting or collapsing while i_pages lock was held, and now go on to call dquot_alloc_block_nodirty() which takes i_lock to update i_blocks. We may well want to take i_lock out of this path later, in the non-quota case even if it's left in the quota case (or perhaps use i_lock instead of shmem's info->lock throughout); but don't get into that at this time. Move the shmem_charge() and shmem_uncharge() calls out from under i_pages lock, accounting the full batch of holes in a single call. Still pass the pages argument to shmem_uncharge(), but it happens now to be unused: shmem_recalc_inode() is designed to account for clean pages freed behind shmem's back, so it gets the accounting right by itself; then the later call to shmem_inode_unacct_blocks() led to imbalance (that WARN_ON(inode->i_blocks) in shmem_evict_inode()). Reported-by: [email protected] Closes: https://lore.kernel.org/lkml/[email protected]/ Reported-by: [email protected] Closes: https://lore.kernel.org/lkml/[email protected]/ Signed-off-by: Hugh Dickins <[email protected]> Tested-by: Carlos Maiolino <[email protected]> Reviewed-by: Carlos Maiolino <[email protected]> Message-Id: <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent de4c0e7 commit 509f006

File tree

3 files changed

+20
-18
lines changed

3 files changed

+20
-18
lines changed

mm/huge_memory.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,7 +2521,7 @@ static void __split_huge_page(struct page *page, struct list_head *list,
25212521
struct address_space *swap_cache = NULL;
25222522
unsigned long offset = 0;
25232523
unsigned int nr = thp_nr_pages(head);
2524-
int i;
2524+
int i, nr_dropped = 0;
25252525

25262526
/* complete memcg works before add pages to LRU */
25272527
split_page_memcg(head, nr);
@@ -2546,7 +2546,7 @@ static void __split_huge_page(struct page *page, struct list_head *list,
25462546
struct folio *tail = page_folio(head + i);
25472547

25482548
if (shmem_mapping(head->mapping))
2549-
shmem_uncharge(head->mapping->host, 1);
2549+
nr_dropped++;
25502550
else if (folio_test_clear_dirty(tail))
25512551
folio_account_cleaned(tail,
25522552
inode_to_wb(folio->mapping->host));
@@ -2583,6 +2583,8 @@ static void __split_huge_page(struct page *page, struct list_head *list,
25832583
}
25842584
local_irq_enable();
25852585

2586+
if (nr_dropped)
2587+
shmem_uncharge(head->mapping->host, nr_dropped);
25862588
remap_page(folio, nr);
25872589

25882590
if (PageSwapCache(head)) {

mm/khugepaged.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,10 +1955,6 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
19551955
goto xa_locked;
19561956
}
19571957
}
1958-
if (!shmem_charge(mapping->host, 1)) {
1959-
result = SCAN_FAIL;
1960-
goto xa_locked;
1961-
}
19621958
nr_none++;
19631959
continue;
19641960
}
@@ -2145,8 +2141,13 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
21452141
*/
21462142
try_to_unmap_flush();
21472143

2148-
if (result != SCAN_SUCCEED)
2144+
if (result == SCAN_SUCCEED && nr_none &&
2145+
!shmem_charge(mapping->host, nr_none))
2146+
result = SCAN_FAIL;
2147+
if (result != SCAN_SUCCEED) {
2148+
nr_none = 0;
21492149
goto rollback;
2150+
}
21502151

21512152
/*
21522153
* The old pages are locked, so they won't change anymore.
@@ -2283,8 +2284,8 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
22832284
if (nr_none) {
22842285
xas_lock_irq(&xas);
22852286
mapping->nrpages -= nr_none;
2286-
shmem_uncharge(mapping->host, nr_none);
22872287
xas_unlock_irq(&xas);
2288+
shmem_uncharge(mapping->host, nr_none);
22882289
}
22892290

22902291
list_for_each_entry_safe(page, tmp, &pagelist, lru) {

mm/shmem.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -424,35 +424,34 @@ static void shmem_recalc_inode(struct inode *inode)
424424
bool shmem_charge(struct inode *inode, long pages)
425425
{
426426
struct shmem_inode_info *info = SHMEM_I(inode);
427-
unsigned long flags;
427+
struct address_space *mapping = inode->i_mapping;
428428

429429
if (shmem_inode_acct_block(inode, pages))
430430
return false;
431431

432432
/* nrpages adjustment first, then shmem_recalc_inode() when balanced */
433-
inode->i_mapping->nrpages += pages;
433+
xa_lock_irq(&mapping->i_pages);
434+
mapping->nrpages += pages;
435+
xa_unlock_irq(&mapping->i_pages);
434436

435-
spin_lock_irqsave(&info->lock, flags);
437+
spin_lock_irq(&info->lock);
436438
info->alloced += pages;
437439
shmem_recalc_inode(inode);
438-
spin_unlock_irqrestore(&info->lock, flags);
440+
spin_unlock_irq(&info->lock);
439441

440442
return true;
441443
}
442444

443445
void shmem_uncharge(struct inode *inode, long pages)
444446
{
445447
struct shmem_inode_info *info = SHMEM_I(inode);
446-
unsigned long flags;
447448

448449
/* nrpages adjustment done by __filemap_remove_folio() or caller */
449450

450-
spin_lock_irqsave(&info->lock, flags);
451-
info->alloced -= pages;
451+
spin_lock_irq(&info->lock);
452452
shmem_recalc_inode(inode);
453-
spin_unlock_irqrestore(&info->lock, flags);
454-
455-
shmem_inode_unacct_blocks(inode, pages);
453+
/* which has called shmem_inode_unacct_blocks() if necessary */
454+
spin_unlock_irq(&info->lock);
456455
}
457456

458457
/*

0 commit comments

Comments
 (0)