Merge tag 'nvme-6.5-2023-06-30' of git://git.infradead.org/nvme into block-6.5
[platform/kernel/linux-starfive.git] / mm / workingset.c
index 8177589..4686ae3 100644 (file)
@@ -255,45 +255,58 @@ static void *lru_gen_eviction(struct folio *folio)
        return pack_shadow(mem_cgroup_id(memcg), pgdat, token, refs);
 }
 
+/*
+ * Tests if the shadow entry is for a folio that was recently evicted.
+ * Fills in @lruvec, @token, @workingset with the values unpacked from shadow.
+ */
+static bool lru_gen_test_recent(void *shadow, bool file, struct lruvec **lruvec,
+                               unsigned long *token, bool *workingset)
+{
+       int memcg_id;
+       unsigned long min_seq;
+       struct mem_cgroup *memcg;
+       struct pglist_data *pgdat;
+
+       unpack_shadow(shadow, &memcg_id, &pgdat, token, workingset);
+
+       memcg = mem_cgroup_from_id(memcg_id);
+       *lruvec = mem_cgroup_lruvec(memcg, pgdat);
+
+       min_seq = READ_ONCE((*lruvec)->lrugen.min_seq[file]);
+       return (*token >> LRU_REFS_WIDTH) == (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH));
+}
+
 static void lru_gen_refault(struct folio *folio, void *shadow)
 {
+       bool recent;
        int hist, tier, refs;
-       int memcg_id;
        bool workingset;
        unsigned long token;
-       unsigned long min_seq;
        struct lruvec *lruvec;
        struct lru_gen_folio *lrugen;
-       struct mem_cgroup *memcg;
-       struct pglist_data *pgdat;
        int type = folio_is_file_lru(folio);
        int delta = folio_nr_pages(folio);
 
-       unpack_shadow(shadow, &memcg_id, &pgdat, &token, &workingset);
-
-       if (pgdat != folio_pgdat(folio))
-               return;
-
        rcu_read_lock();
 
-       memcg = folio_memcg_rcu(folio);
-       if (memcg_id != mem_cgroup_id(memcg))
+       recent = lru_gen_test_recent(shadow, type, &lruvec, &token, &workingset);
+       if (lruvec != folio_lruvec(folio))
                goto unlock;
 
-       lruvec = mem_cgroup_lruvec(memcg, pgdat);
-       lrugen = &lruvec->lrugen;
+       mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + type, delta);
 
-       min_seq = READ_ONCE(lrugen->min_seq[type]);
-       if ((token >> LRU_REFS_WIDTH) != (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH)))
+       if (!recent)
                goto unlock;
 
-       hist = lru_hist_from_seq(min_seq);
+       lrugen = &lruvec->lrugen;
+
+       hist = lru_hist_from_seq(READ_ONCE(lrugen->min_seq[type]));
        /* see the comment in folio_lru_refs() */
        refs = (token & (BIT(LRU_REFS_WIDTH) - 1)) + workingset;
        tier = lru_tier_from_refs(refs);
 
        atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]);
-       mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + type, delta);
+       mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta);
 
        /*
         * Count the following two cases as stalls:
@@ -317,6 +330,12 @@ static void *lru_gen_eviction(struct folio *folio)
        return NULL;
 }
 
+static bool lru_gen_test_recent(void *shadow, bool file, struct lruvec **lruvec,
+                               unsigned long *token, bool *workingset)
+{
+       return false;
+}
+
 static void lru_gen_refault(struct folio *folio, void *shadow)
 {
 }
@@ -385,42 +404,33 @@ void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg)
 }
 
 /**
- * workingset_refault - Evaluate the refault of a previously evicted folio.
- * @folio: The freshly allocated replacement folio.
- * @shadow: Shadow entry of the evicted folio.
- *
- * Calculates and evaluates the refault distance of the previously
- * evicted folio in the context of the node and the memcg whose memory
- * pressure caused the eviction.
+ * workingset_test_recent - tests if the shadow entry is for a folio that was
+ * recently evicted. Also fills in @workingset with the value unpacked from
+ * shadow.
+ * @shadow: the shadow entry to be tested.
+ * @file: whether the corresponding folio is from the file lru.
+ * @workingset: where the workingset value unpacked from shadow should
+ * be stored.
+ *
+ * Return: true if the shadow is for a recently evicted folio; false otherwise.
  */
-void workingset_refault(struct folio *folio, void *shadow)
+bool workingset_test_recent(void *shadow, bool file, bool *workingset)
 {
-       bool file = folio_is_file_lru(folio);
        struct mem_cgroup *eviction_memcg;
        struct lruvec *eviction_lruvec;
        unsigned long refault_distance;
        unsigned long workingset_size;
-       struct pglist_data *pgdat;
-       struct mem_cgroup *memcg;
-       unsigned long eviction;
-       struct lruvec *lruvec;
        unsigned long refault;
-       bool workingset;
        int memcgid;
-       long nr;
+       struct pglist_data *pgdat;
+       unsigned long eviction;
 
-       if (lru_gen_enabled()) {
-               lru_gen_refault(folio, shadow);
-               return;
-       }
+       if (lru_gen_enabled())
+               return lru_gen_test_recent(shadow, file, &eviction_lruvec, &eviction, workingset);
 
-       unpack_shadow(shadow, &memcgid, &pgdat, &eviction, &workingset);
+       unpack_shadow(shadow, &memcgid, &pgdat, &eviction, workingset);
        eviction <<= bucket_order;
 
-       /* Flush stats (and potentially sleep) before holding RCU read lock */
-       mem_cgroup_flush_stats_ratelimited();
-
-       rcu_read_lock();
        /*
         * Look up the memcg associated with the stored ID. It might
         * have been deleted since the folio's eviction.
@@ -439,7 +449,8 @@ void workingset_refault(struct folio *folio, void *shadow)
         */
        eviction_memcg = mem_cgroup_from_id(memcgid);
        if (!mem_cgroup_disabled() && !eviction_memcg)
-               goto out;
+               return false;
+
        eviction_lruvec = mem_cgroup_lruvec(eviction_memcg, pgdat);
        refault = atomic_long_read(&eviction_lruvec->nonresident_age);
 
@@ -462,20 +473,6 @@ void workingset_refault(struct folio *folio, void *shadow)
        refault_distance = (refault - eviction) & EVICTION_MASK;
 
        /*
-        * The activation decision for this folio is made at the level
-        * where the eviction occurred, as that is where the LRU order
-        * during folio reclaim is being determined.
-        *
-        * However, the cgroup that will own the folio is the one that
-        * is actually experiencing the refault event.
-        */
-       nr = folio_nr_pages(folio);
-       memcg = folio_memcg(folio);
-       pgdat = folio_pgdat(folio);
-       lruvec = mem_cgroup_lruvec(memcg, pgdat);
-
-       mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + file, nr);
-       /*
         * Compare the distance to the existing workingset size. We
         * don't activate pages that couldn't stay resident even if
         * all the memory was available to the workingset. Whether
@@ -495,7 +492,54 @@ void workingset_refault(struct folio *folio, void *shadow)
                                                     NR_INACTIVE_ANON);
                }
        }
-       if (refault_distance > workingset_size)
+
+       return refault_distance <= workingset_size;
+}
+
+/**
+ * workingset_refault - Evaluate the refault of a previously evicted folio.
+ * @folio: The freshly allocated replacement folio.
+ * @shadow: Shadow entry of the evicted folio.
+ *
+ * Calculates and evaluates the refault distance of the previously
+ * evicted folio in the context of the node and the memcg whose memory
+ * pressure caused the eviction.
+ */
+void workingset_refault(struct folio *folio, void *shadow)
+{
+       bool file = folio_is_file_lru(folio);
+       struct pglist_data *pgdat;
+       struct mem_cgroup *memcg;
+       struct lruvec *lruvec;
+       bool workingset;
+       long nr;
+
+       if (lru_gen_enabled()) {
+               lru_gen_refault(folio, shadow);
+               return;
+       }
+
+       /* Flush stats (and potentially sleep) before holding RCU read lock */
+       mem_cgroup_flush_stats_ratelimited();
+
+       rcu_read_lock();
+
+       /*
+        * The activation decision for this folio is made at the level
+        * where the eviction occurred, as that is where the LRU order
+        * during folio reclaim is being determined.
+        *
+        * However, the cgroup that will own the folio is the one that
+        * is actually experiencing the refault event.
+        */
+       nr = folio_nr_pages(folio);
+       memcg = folio_memcg(folio);
+       pgdat = folio_pgdat(folio);
+       lruvec = mem_cgroup_lruvec(memcg, pgdat);
+
+       mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + file, nr);
+
+       if (!workingset_test_recent(shadow, file, &workingset))
                goto out;
 
        folio_set_active(folio);