Re: [PATCH v5 9/9] mm: switch deferred split shrinker to list_lru
From: Wei Yang
Date: Sat Jun 20 2026 - 03:39:27 EST
On Wed, May 27, 2026 at 04:45:16PM -0400, Johannes Weiner wrote:
[...]
>@@ -3890,34 +3804,43 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
> struct folio *end_folio = folio_next(folio);
> struct folio *new_folio, *next;
> int old_order = folio_order(folio);
>+ struct list_lru_one *lru;
>+ bool dequeue_deferred;
> int ret = 0;
>- struct deferred_split *ds_queue;
>
> VM_WARN_ON_ONCE(!mapping && end);
>- /* Prevent deferred_split_scan() touching ->_refcount */
>- ds_queue = folio_split_queue_lock(folio);
>+ /*
>+ * If this folio can be on the deferred split queue, lock out
>+ * the shrinker before freezing the ref. If the shrinker sees
>+ * a 0-ref folio, it assumes it beat folio_put() to the list
>+ * lock and must clean up the LRU state - the same dequeue we
>+ * will do below as part of the split.
>+ */
>+ dequeue_deferred = folio_test_anon(folio) && old_order > 1;
Looking at __folio_remove_rmap(), we check !folio_is_device_private() before
deferred_split_folio(). __folio_freeze_and_split_unmapped() is used in
folio_split_unmapped(). According to its comment, it could take device-private
folio.
This means for device-private folio, we still lock lru_list and try to remove
it from deferred_split_lru. The good news is this doesn't harm the system, but
does some extra work.
Would it be better to add !folio_is_device_private() here?
The purpose to lock here is to prevent shrinker seeing ref-0 folio. Since
device-private folio is not on deferred_split_lru, shrink won't see it.
>+ if (dequeue_deferred) {
>+ struct mem_cgroup *memcg;
>+
>+ rcu_read_lock();
>+ memcg = folio_memcg(folio);
>+ lru = list_lru_lock(&deferred_split_lru,
>+ folio_nid(folio), &memcg);
>+ }
> if (folio_ref_freeze(folio, folio_cache_ref_count(folio) + 1)) {
> struct swap_cluster_info *ci = NULL;
> struct lruvec *lruvec;
>
>- if (old_order > 1) {
>- if (!list_empty(&folio->_deferred_list)) {
>- ds_queue->split_queue_len--;
>- /*
>- * Reinitialize page_deferred_list after removing the
>- * page from the split_queue, otherwise a subsequent
>- * split will see list corruption when checking the
>- * page_deferred_list.
>- */
>- list_del_init(&folio->_deferred_list);
>- }
>+ if (dequeue_deferred) {
>+ __list_lru_del(&deferred_split_lru, lru,
>+ &folio->_deferred_list, folio_nid(folio));
> if (folio_test_partially_mapped(folio)) {
> folio_clear_partially_mapped(folio);
> mod_mthp_stat(old_order,
> MTHP_STAT_NR_ANON_PARTIALLY_MAPPED, -1);
> }
>+ list_lru_unlock(lru);
>+ rcu_read_unlock();
> }
>- split_queue_unlock(ds_queue);
>+
> if (mapping) {
> int nr = folio_nr_pages(folio);
>
>@@ -4017,7 +3940,10 @@ static int __folio_freeze_and_split_unmapped(struct folio *folio, unsigned int n
> if (ci)
> swap_cluster_unlock(ci);
> } else {
>- split_queue_unlock(ds_queue);
>+ if (dequeue_deferred) {
>+ list_lru_unlock(lru);
>+ rcu_read_unlock();
>+ }
> return -EAGAIN;
> }
>
--
Wei Yang
Help you, Help me