We should make thp deferred split queue lock safe when LRU pages
reparented. Similar to lock_page_lruvec{_irqsave, _irq}(), we
introduce lock/unlock_split_queue{_irqsave}() to make the deferred
split queue lock easier to be reparented.

And in the next patch, we can use a similar approach (just like
lruvec lock did) to make thp deferred split queue lock safe when
the LRU pages reparented.

Signed-off-by: Muchun Song <songmuc...@bytedance.com>
---
 mm/huge_memory.c | 97 +++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 75 insertions(+), 22 deletions(-)

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 395c75111d33..186dc11e8992 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -486,25 +486,76 @@ pmd_t maybe_pmd_mkwrite(pmd_t pmd, struct vm_area_struct 
*vma)
 }
 
 #ifdef CONFIG_MEMCG
-static inline struct deferred_split *get_deferred_split_queue(struct page 
*page)
+static inline struct mem_cgroup *split_queue_to_memcg(struct deferred_split 
*queue)
 {
-       struct mem_cgroup *memcg = page_memcg(compound_head(page));
-       struct pglist_data *pgdat = NODE_DATA(page_to_nid(page));
+       return container_of(queue, struct mem_cgroup, deferred_split_queue);
+}
+
+static struct deferred_split *lock_split_queue(struct page *page)
+{
+       struct deferred_split *queue;
+       struct mem_cgroup *memcg;
 
+       memcg = page_memcg(compound_head(page));
        if (memcg)
-               return &memcg->deferred_split_queue;
+               queue = &memcg->deferred_split_queue;
        else
-               return &pgdat->deferred_split_queue;
+               queue = &NODE_DATA(page_to_nid(page))->deferred_split_queue;
+       spin_lock(&queue->split_queue_lock);
+
+       return queue;
+}
+
+static struct deferred_split *lock_split_queue_irqsave(struct page *page,
+                                                      unsigned long *flags)
+{
+       struct deferred_split *queue;
+       struct mem_cgroup *memcg;
+
+       memcg = page_memcg(compound_head(page));
+       if (memcg)
+               queue = &memcg->deferred_split_queue;
+       else
+               queue = &NODE_DATA(page_to_nid(page))->deferred_split_queue;
+       spin_lock_irqsave(&queue->split_queue_lock, *flags);
+
+       return queue;
 }
 #else
-static inline struct deferred_split *get_deferred_split_queue(struct page 
*page)
+static struct deferred_split *lock_split_queue(struct page *page)
 {
-       struct pglist_data *pgdat = NODE_DATA(page_to_nid(page));
+       struct deferred_split *queue;
 
-       return &pgdat->deferred_split_queue;
+       queue = &NODE_DATA(page_to_nid(page))->deferred_split_queue;
+       spin_lock(&queue->split_queue_lock);
+
+       return queue;
+}
+
+static struct deferred_split *lock_split_queue_irqsave(struct page *page,
+                                                      unsigned long *flags)
+
+{
+       struct deferred_split *queue;
+
+       queue = &NODE_DATA(page_to_nid(page))->deferred_split_queue;
+       spin_lock_irqsave(&queue->split_queue_lock, *flags);
+
+       return queue;
 }
 #endif
 
+static inline void unlock_split_queue(struct deferred_split *queue)
+{
+       spin_unlock(&queue->split_queue_lock);
+}
+
+static inline void unlock_split_queue_irqrestore(struct deferred_split *queue,
+                                                unsigned long flags)
+{
+       spin_unlock_irqrestore(&queue->split_queue_lock, flags);
+}
+
 void prep_transhuge_page(struct page *page)
 {
        /*
@@ -2668,7 +2719,7 @@ bool can_split_huge_page(struct page *page, int 
*pextra_pins)
 int split_huge_page_to_list(struct page *page, struct list_head *list)
 {
        struct page *head = compound_head(page);
-       struct deferred_split *ds_queue = get_deferred_split_queue(head);
+       struct deferred_split *ds_queue;
        struct anon_vma *anon_vma = NULL;
        struct address_space *mapping = NULL;
        int count, mapcount, extra_pins, ret;
@@ -2747,7 +2798,7 @@ int split_huge_page_to_list(struct page *page, struct 
list_head *list)
        }
 
        /* Prevent deferred_split_scan() touching ->_refcount */
-       spin_lock(&ds_queue->split_queue_lock);
+       ds_queue = lock_split_queue(head);
        count = page_count(head);
        mapcount = total_mapcount(head);
        if (!mapcount && page_ref_freeze(head, 1 + extra_pins)) {
@@ -2755,7 +2806,7 @@ int split_huge_page_to_list(struct page *page, struct 
list_head *list)
                        ds_queue->split_queue_len--;
                        list_del(page_deferred_list(head));
                }
-               spin_unlock(&ds_queue->split_queue_lock);
+               unlock_split_queue(ds_queue);
                if (mapping) {
                        int nr = thp_nr_pages(head);
 
@@ -2778,7 +2829,7 @@ int split_huge_page_to_list(struct page *page, struct 
list_head *list)
                        dump_page(page, "total_mapcount(head) > 0");
                        BUG();
                }
-               spin_unlock(&ds_queue->split_queue_lock);
+               unlock_split_queue(ds_queue);
 fail:          if (mapping)
                        xa_unlock(&mapping->i_pages);
                local_irq_enable();
@@ -2800,24 +2851,21 @@ fail:           if (mapping)
 
 void free_transhuge_page(struct page *page)
 {
-       struct deferred_split *ds_queue = get_deferred_split_queue(page);
+       struct deferred_split *ds_queue;
        unsigned long flags;
 
-       spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
+       ds_queue = lock_split_queue_irqsave(page, &flags);
        if (!list_empty(page_deferred_list(page))) {
                ds_queue->split_queue_len--;
                list_del(page_deferred_list(page));
        }
-       spin_unlock_irqrestore(&ds_queue->split_queue_lock, flags);
+       unlock_split_queue_irqrestore(ds_queue, flags);
        free_compound_page(page);
 }
 
 void deferred_split_huge_page(struct page *page)
 {
-       struct deferred_split *ds_queue = get_deferred_split_queue(page);
-#ifdef CONFIG_MEMCG
-       struct mem_cgroup *memcg = page_memcg(compound_head(page));
-#endif
+       struct deferred_split *ds_queue;
        unsigned long flags;
 
        VM_BUG_ON_PAGE(!PageTransHuge(page), page);
@@ -2835,18 +2883,23 @@ void deferred_split_huge_page(struct page *page)
        if (PageSwapCache(page))
                return;
 
-       spin_lock_irqsave(&ds_queue->split_queue_lock, flags);
+       ds_queue = lock_split_queue_irqsave(page, &flags);
        if (list_empty(page_deferred_list(page))) {
                count_vm_event(THP_DEFERRED_SPLIT_PAGE);
                list_add_tail(page_deferred_list(page), &ds_queue->split_queue);
                ds_queue->split_queue_len++;
+
 #ifdef CONFIG_MEMCG
-               if (memcg)
+               if (page_memcg(page)) {
+                       struct mem_cgroup *memcg;
+
+                       memcg = split_queue_to_memcg(ds_queue);
                        memcg_set_shrinker_bit(memcg, page_to_nid(page),
                                               deferred_split_shrinker.id);
+               }
 #endif
        }
-       spin_unlock_irqrestore(&ds_queue->split_queue_lock, flags);
+       unlock_split_queue_irqrestore(ds_queue, flags);
 }
 
 static unsigned long deferred_split_count(struct shrinker *shrink,
-- 
2.11.0

Reply via email to