On Wed, Dec 14, 2022 at 02:51:23PM -0800, Yuanchu Xie <yuanchu@google.com> wrote:
> +static int memory_page_idle_age_format(struct mem_cgroup *root,
> + struct seq_file *m)
> +{
> + struct mem_cgroup *memcg;
> + unsigned long *table;
[...]
> + table = kmalloc_array(PAGE_IDLE_AGE_NR_RANGES * nr_node_ids *
> + ANON_AND_FILE,
> + sizeof(*table), __GFP_ZERO | GFP_KERNEL);
> +
[...]
> + memory_page_idle_age_print(m, table);
> + return 0;
FTR, the table seems leaked here.
Michal
@@ -1655,6 +1655,130 @@ void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)
pr_info("%s", buf);
}
+#ifdef CONFIG_LRU_GEN
+static const unsigned long page_idle_age_ranges[] = {
+ 1, 2, 5, 10, 20, 30, 45, 60, 90, 120, 180,
+ 240, 360, 480, 720, 960, 1440, 1920, 2880, 3840, -1
+};
+
+#define PAGE_IDLE_AGE_NR_RANGES ARRAY_SIZE(page_idle_age_ranges)
+
+static unsigned int lru_gen_time_to_page_idle_age_range(unsigned long timestamp)
+{
+ unsigned int i;
+ unsigned long gen_age = jiffies_to_msecs(jiffies - timestamp) / MSEC_PER_SEC;
+
+ for (i = 0; i < PAGE_IDLE_AGE_NR_RANGES - 1; ++i)
+ if (gen_age <= page_idle_age_ranges[i])
+ return i;
+
+ return PAGE_IDLE_AGE_NR_RANGES - 1;
+}
+
+static void lru_gen_fill_page_idle_age_table(unsigned long *table,
+ struct lru_gen_struct *lrugen,
+ int nid)
+{
+ unsigned long max_seq = READ_ONCE(lrugen->max_seq);
+ unsigned long min_seq[ANON_AND_FILE] = {
+ READ_ONCE(lrugen->min_seq[LRU_GEN_ANON]),
+ READ_ONCE(lrugen->min_seq[LRU_GEN_FILE]),
+ };
+ unsigned long seq;
+ unsigned int pagetype;
+
+ /*
+ * what do we want to do here?
+ * iterate over all the generations, for each anon and file
+ */
+
+ for (pagetype = LRU_GEN_ANON; pagetype < ANON_AND_FILE; ++pagetype) {
+ for (seq = min_seq[pagetype]; seq <= max_seq; ++seq) {
+ unsigned int zone;
+ unsigned int gen = lru_gen_from_seq(seq);
+ unsigned int idle_age = lru_gen_time_to_page_idle_age_range(
+ READ_ONCE(lrugen->timestamps[gen]));
+ unsigned long page_count = 0;
+
+ for (zone = 0; zone < MAX_NR_ZONES; ++zone) {
+ page_count += READ_ONCE(
+ lrugen->nr_pages[gen][pagetype][zone]);
+ }
+ table[pagetype * PAGE_IDLE_AGE_NR_RANGES *
+ nr_node_ids +
+ PAGE_IDLE_AGE_NR_RANGES * nid + idle_age] +=
+ page_count;
+ }
+ }
+}
+
+static void memory_page_idle_age_print(struct seq_file *m, unsigned long *table)
+{
+ static const char *type_str[ANON_AND_FILE] = { "anon", "file" };
+ unsigned int i, nid, pagetype;
+ unsigned int lower = 0;
+
+ for (i = 0; i < PAGE_IDLE_AGE_NR_RANGES; ++i) {
+ unsigned int upper = page_idle_age_ranges[i];
+
+ for (pagetype = LRU_GEN_ANON; pagetype < ANON_AND_FILE;
+ ++pagetype) {
+ if (upper == -1)
+ seq_printf(m, "%u-inf %s", lower,
+ type_str[pagetype]);
+ else
+ seq_printf(m, "%u-%u %s", lower, upper,
+ type_str[pagetype]);
+ for_each_node_state(nid, N_MEMORY) {
+ unsigned long page_count = table
+ [pagetype *
+ PAGE_IDLE_AGE_NR_RANGES *
+ nr_node_ids +
+ PAGE_IDLE_AGE_NR_RANGES * nid +
+ i];
+ seq_printf(m, " N%u=%lu", nid, page_count);
+ }
+ seq_puts(m, "\n");
+ }
+
+ lower = upper;
+ }
+}
+
+static int memory_page_idle_age_format(struct mem_cgroup *root,
+ struct seq_file *m)
+{
+ struct mem_cgroup *memcg;
+ unsigned long *table;
+
+ /*
+ * table contains PAGE_IDLE_AGE_NR_RANGES entries
+ * per node per pagetype
+ */
+ table = kmalloc_array(PAGE_IDLE_AGE_NR_RANGES * nr_node_ids *
+ ANON_AND_FILE,
+ sizeof(*table), __GFP_ZERO | GFP_KERNEL);
+
+ if (!table)
+ return -ENOMEM;
+
+ memcg = mem_cgroup_iter(root, NULL, NULL);
+ do {
+ int nid;
+
+ for_each_node_state(nid, N_MEMORY) {
+ struct lru_gen_struct *lrugen =
+ &memcg->nodeinfo[nid]->lruvec.lrugen;
+
+ lru_gen_fill_page_idle_age_table(table, lrugen, nid);
+ }
+ } while ((memcg = mem_cgroup_iter(root, memcg, NULL)));
+
+ memory_page_idle_age_print(m, table);
+ return 0;
+}
+#endif /* CONFIG_LRU_GEN */
+
/*
* Return the memory (and swap, if configured) limit for a memcg.
*/
@@ -6571,6 +6695,13 @@ static ssize_t memory_oom_group_write(struct kernfs_open_file *of,
}
#ifdef CONFIG_LRU_GEN
+static int memory_page_idle_age_show(struct seq_file *m, void *v)
+{
+ struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
+
+ return memory_page_idle_age_format(memcg, m);
+}
+
static int memory_periodic_aging_show(struct seq_file *m, void *v)
{
unsigned int interval = kold_get_interval();
@@ -6724,6 +6855,11 @@ static struct cftype memory_files[] = {
.write = memory_reclaim,
},
#ifdef CONFIG_LRU_GEN
+ {
+ .name = "page_idle_age",
+ .flags = CFTYPE_NS_DELEGATABLE,
+ .seq_show = memory_page_idle_age_show,
+ },
{
.name = "periodic_aging",
.flags = CFTYPE_ONLY_ON_ROOT,