When a system is under memory pressure and kswapd kicks in, a working set report is produced. The userspace program polling on the histogram file is notified of the new report. The report threshold acts as a rate-limiting mechanism to prevent the system from generating reports too frequently. Signed-off-by: T.J. Alumbaugh <talumbau@xxxxxxxxxx> Signed-off-by: Yuanchu Xie <yuanchu@xxxxxxxxxx> --- include/linux/wsr.h | 2 ++ mm/vmscan.c | 37 +++++++++++++++++++++++++++++++++++++ mm/wsr.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/include/linux/wsr.h b/include/linux/wsr.h index a86105468c710..85c901ce026b9 100644 --- a/include/linux/wsr.h +++ b/include/linux/wsr.h @@ -26,7 +26,9 @@ struct ws_bin { struct wsr { /* protects bins */ struct mutex bins_lock; + struct kernfs_node *notifier; unsigned long timestamp; + unsigned long report_threshold; unsigned long refresh_threshold; struct ws_bin bins[MAX_NR_BINS]; }; diff --git a/mm/vmscan.c b/mm/vmscan.c index 66c5df2a7f65b..c56fddcec88fb 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4559,6 +4559,8 @@ static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned return true; } +static void report_ws(struct pglist_data *pgdat, struct scan_control *sc); + /* to protect the working set of the last N jiffies */ static unsigned long lru_gen_min_ttl __read_mostly; @@ -4570,6 +4572,8 @@ static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) VM_WARN_ON_ONCE(!current_is_kswapd()); + report_ws(pgdat, sc); + sc->last_reclaimed = sc->nr_reclaimed; /* @@ -5933,6 +5937,39 @@ void wsr_refresh(struct wsr *wsr, struct mem_cgroup *root, } } +static void report_ws(struct pglist_data *pgdat, struct scan_control *sc) +{ + static DEFINE_RATELIMIT_STATE(rate, HZ, 3); + + struct mem_cgroup *memcg = sc->target_mem_cgroup; + struct wsr *wsr = lruvec_wsr(mem_cgroup_lruvec(memcg, pgdat)); + unsigned long threshold; + + threshold = READ_ONCE(wsr->report_threshold); + + if (sc->priority == DEF_PRIORITY) + return; + + if (READ_ONCE(wsr->bins->idle_age) == -1) + return; + + if (!threshold || time_is_after_jiffies(wsr->timestamp + threshold)) + return; + + if (!__ratelimit(&rate)) + return; + + if (!mutex_trylock(&wsr->bins_lock)) + return; + + refresh_wsr(wsr, memcg, pgdat, sc, 0); + WRITE_ONCE(wsr->timestamp, jiffies); + + mutex_unlock(&wsr->bins_lock); + + if (wsr->notifier) + kernfs_notify(wsr->notifier); +} #endif /* CONFIG_WSR */ #else /* !CONFIG_LRU_GEN */ diff --git a/mm/wsr.c b/mm/wsr.c index ee295d164461e..cd045ade5e9ba 100644 --- a/mm/wsr.c +++ b/mm/wsr.c @@ -24,6 +24,7 @@ void wsr_init(struct lruvec *lruvec) mutex_init(&wsr->bins_lock); wsr->bins[0].idle_age = -1; + wsr->notifier = NULL; } void wsr_destroy(struct lruvec *lruvec) @@ -184,6 +185,30 @@ static struct wsr *kobj_to_wsr(struct kobject *kobj) return lruvec_wsr(mem_cgroup_lruvec(NULL, kobj_to_pgdat(kobj))); } +static ssize_t report_ms_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct wsr *wsr = kobj_to_wsr(kobj); + unsigned long threshold = READ_ONCE(wsr->report_threshold); + + return sysfs_emit(buf, "%u\n", jiffies_to_msecs(threshold)); +} + +static ssize_t report_ms_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + unsigned int msecs; + struct wsr *wsr = kobj_to_wsr(kobj); + + if (kstrtouint(buf, 0, &msecs)) + return -EINVAL; + + WRITE_ONCE(wsr->report_threshold, msecs_to_jiffies(msecs)); + + return len; +} + +static struct kobj_attribute report_ms_attr = __ATTR_RW(report_ms); static ssize_t refresh_ms_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -290,6 +315,7 @@ static ssize_t histogram_show(struct kobject *kobj, struct kobj_attribute *attr, static struct kobj_attribute histogram_attr = __ATTR_RO(histogram); static struct attribute *wsr_attrs[] = { + &report_ms_attr.attr, &refresh_ms_attr.attr, &intervals_ms_attr.attr, &histogram_attr.attr, @@ -318,6 +344,8 @@ void wsr_register_node(struct node *node) pr_warn("WSR failed to created group"); return; } + + wsr->notifier = kernfs_walk_and_get(kobj->sd, "wsr/histogram"); } void wsr_unregister_node(struct node *node) @@ -329,6 +357,7 @@ void wsr_unregister_node(struct node *node) return; wsr = kobj_to_wsr(kobj); + kernfs_put(wsr->notifier); sysfs_remove_group(kobj, &wsr_attr_group); wsr_destroy(mem_cgroup_lruvec(NULL, kobj_to_pgdat(kobj))); } -- 2.41.0.162.gfafddb0af9-goog