[lvc-project] [PATCH 5.10 2/2] hugetlbfs: fix hugetlbfs_statfs() locking

Mikhail Ukhin mish.uxin2012 at yandex.ru
Wed May 22 22:45:07 MSK 2024


From: Mike Kravetz <mike.kravetz at oracle.com>

Commit 4b25f030ae69ba710eff587cabb4c57cb7e7a8a1 upstream.

Commit c77c0a8ac4c5 ("mm/hugetlb: defer freeing of huge pages if in
non-task context") was added to address the issue of free_huge_page being
called from irq context.  That commit hands off free_huge_page processing
to a workqueue if !in_task.  However, this doesn't cover all the cases as
pointed out by 0day bot lockdep report [1].

:  Possible interrupt unsafe locking scenario:
:
:        CPU0                    CPU1
:        ----                    ----
:   lock(hugetlb_lock);
:                                local_irq_disable();
:                                lock(slock-AF_INET);
:                                lock(hugetlb_lock);
:   <Interrupt>
:     lock(slock-AF_INET);

Shakeel has later explained that this is very likely TCP TX zerocopy from
hugetlb pages scenario when the networking code drops a last reference to
hugetlb page while having IRQ disabled.  Hugetlb freeing path doesn't
disable IRQ while holding hugetlb_lock so a lock dependency chain can lead
to a deadlock.

This commit addresses the issue by doing the following:
- Make hugetlb_lock irq safe.  This is mostly a simple process of
  changing spin_*lock calls to spin_*lock_irq* calls.
- Make subpool lock irq safe in a similar manner.
- Revert the !in_task check and workqueue handoff.

[1] https://lore.kernel.org/linux-mm/000000000000f1c03b05bc43aadc@google.com/

Link: https://lkml.kernel.org/r/20210409205254.242291-8-mike.kravetz@oracle.com
Signed-off-by: Mike Kravetz <mike.kravetz at oracle.com>

[After commit db71ef79b59b ("hugetlb: make free_huge_page irq safe"), the
subpool lock should be locked with spin_lock_irq() and all call sites was
modified as such, except for the ones in hugetlbfs_statfs()]
Signed-off-by: Mikhail Ivanov <iwanov-23 at bk.ru>
Signed-off-by: Mikhail Ukhin <mish.uxin2012 at yandex.ru>

---
 fs/hugetlbfs/inode.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index bf3cda498962..b6440007116e 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -1069,12 +1069,12 @@ static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf)
                 if (sbinfo->spool) {
                         long free_pages;
 
- spin_lock(&sbinfo->spool->lock);
+ spin_lock_irq(&sbinfo->spool->lock);
                         buf->f_blocks = sbinfo->spool->max_hpages;
                         free_pages = sbinfo->spool->max_hpages
                                 - sbinfo->spool->used_hpages;
                         buf->f_bavail = buf->f_bfree = free_pages;
- spin_unlock(&sbinfo->spool->lock);
+ spin_unlock_irq(&sbinfo->spool->lock);
                         buf->f_files = sbinfo->max_inodes;
                         buf->f_ffree = sbinfo->free_inodes;
                 }
-- 
2.25.1





More information about the lvc-project mailing list