1369c415eSSeongJae Park // SPDX-License-Identifier: GPL-2.0 2369c415eSSeongJae Park /* 3369c415eSSeongJae Park * Shows data access monitoring resutls in simple metrics. 4369c415eSSeongJae Park */ 5369c415eSSeongJae Park 6369c415eSSeongJae Park #define pr_fmt(fmt) "damon-stat: " fmt 7369c415eSSeongJae Park 8369c415eSSeongJae Park #include <linux/damon.h> 9369c415eSSeongJae Park #include <linux/init.h> 10369c415eSSeongJae Park #include <linux/kernel.h> 11369c415eSSeongJae Park #include <linux/module.h> 12369c415eSSeongJae Park #include <linux/sort.h> 13369c415eSSeongJae Park 14369c415eSSeongJae Park #ifdef MODULE_PARAM_PREFIX 15369c415eSSeongJae Park #undef MODULE_PARAM_PREFIX 16369c415eSSeongJae Park #endif 17369c415eSSeongJae Park #define MODULE_PARAM_PREFIX "damon_stat." 18369c415eSSeongJae Park 19369c415eSSeongJae Park static int damon_stat_enabled_store( 20369c415eSSeongJae Park const char *val, const struct kernel_param *kp); 21369c415eSSeongJae Park 22369c415eSSeongJae Park static const struct kernel_param_ops enabled_param_ops = { 23369c415eSSeongJae Park .set = damon_stat_enabled_store, 24369c415eSSeongJae Park .get = param_get_bool, 25369c415eSSeongJae Park }; 26369c415eSSeongJae Park 27369c415eSSeongJae Park static bool enabled __read_mostly = IS_ENABLED( 28369c415eSSeongJae Park CONFIG_DAMON_STAT_ENABLED_DEFAULT); 29369c415eSSeongJae Park module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); 30369c415eSSeongJae Park MODULE_PARM_DESC(enabled, "Enable of disable DAMON_STAT"); 31369c415eSSeongJae Park 32*fabdd1e9SSeongJae Park static unsigned long estimated_memory_bandwidth __read_mostly; 33*fabdd1e9SSeongJae Park module_param(estimated_memory_bandwidth, ulong, 0400); 34*fabdd1e9SSeongJae Park MODULE_PARM_DESC(estimated_memory_bandwidth, 35*fabdd1e9SSeongJae Park "Estimated memory bandwidth usage in bytes per second"); 36*fabdd1e9SSeongJae Park 37369c415eSSeongJae Park static struct damon_ctx *damon_stat_context; 38369c415eSSeongJae Park 39*fabdd1e9SSeongJae Park static void damon_stat_set_estimated_memory_bandwidth(struct damon_ctx *c) 40*fabdd1e9SSeongJae Park { 41*fabdd1e9SSeongJae Park struct damon_target *t; 42*fabdd1e9SSeongJae Park struct damon_region *r; 43*fabdd1e9SSeongJae Park unsigned long access_bytes = 0; 44*fabdd1e9SSeongJae Park 45*fabdd1e9SSeongJae Park damon_for_each_target(t, c) { 46*fabdd1e9SSeongJae Park damon_for_each_region(r, t) 47*fabdd1e9SSeongJae Park access_bytes += (r->ar.end - r->ar.start) * 48*fabdd1e9SSeongJae Park r->nr_accesses; 49*fabdd1e9SSeongJae Park } 50*fabdd1e9SSeongJae Park estimated_memory_bandwidth = access_bytes * USEC_PER_MSEC * 51*fabdd1e9SSeongJae Park MSEC_PER_SEC / c->attrs.aggr_interval; 52*fabdd1e9SSeongJae Park } 53*fabdd1e9SSeongJae Park 54*fabdd1e9SSeongJae Park static int damon_stat_after_aggregation(struct damon_ctx *c) 55*fabdd1e9SSeongJae Park { 56*fabdd1e9SSeongJae Park static unsigned long last_refresh_jiffies; 57*fabdd1e9SSeongJae Park 58*fabdd1e9SSeongJae Park /* avoid unnecessarily frequent stat update */ 59*fabdd1e9SSeongJae Park if (time_before_eq(jiffies, last_refresh_jiffies + 60*fabdd1e9SSeongJae Park msecs_to_jiffies(5 * MSEC_PER_SEC))) 61*fabdd1e9SSeongJae Park return 0; 62*fabdd1e9SSeongJae Park last_refresh_jiffies = jiffies; 63*fabdd1e9SSeongJae Park 64*fabdd1e9SSeongJae Park damon_stat_set_estimated_memory_bandwidth(c); 65*fabdd1e9SSeongJae Park return 0; 66*fabdd1e9SSeongJae Park } 67*fabdd1e9SSeongJae Park 68369c415eSSeongJae Park static struct damon_ctx *damon_stat_build_ctx(void) 69369c415eSSeongJae Park { 70369c415eSSeongJae Park struct damon_ctx *ctx; 71369c415eSSeongJae Park struct damon_attrs attrs; 72369c415eSSeongJae Park struct damon_target *target; 73369c415eSSeongJae Park unsigned long start = 0, end = 0; 74369c415eSSeongJae Park 75369c415eSSeongJae Park ctx = damon_new_ctx(); 76369c415eSSeongJae Park if (!ctx) 77369c415eSSeongJae Park return NULL; 78369c415eSSeongJae Park attrs = (struct damon_attrs) { 79369c415eSSeongJae Park .sample_interval = 5 * USEC_PER_MSEC, 80369c415eSSeongJae Park .aggr_interval = 100 * USEC_PER_MSEC, 81369c415eSSeongJae Park .ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC, 82369c415eSSeongJae Park .min_nr_regions = 10, 83369c415eSSeongJae Park .max_nr_regions = 1000, 84369c415eSSeongJae Park }; 85369c415eSSeongJae Park /* 86369c415eSSeongJae Park * auto-tune sampling and aggregation interval aiming 4% DAMON-observed 87369c415eSSeongJae Park * accesses ratio, keeping sampling interval in [5ms, 10s] range. 88369c415eSSeongJae Park */ 89369c415eSSeongJae Park attrs.intervals_goal = (struct damon_intervals_goal) { 90369c415eSSeongJae Park .access_bp = 400, .aggrs = 3, 91369c415eSSeongJae Park .min_sample_us = 5000, .max_sample_us = 10000000, 92369c415eSSeongJae Park }; 93369c415eSSeongJae Park if (damon_set_attrs(ctx, &attrs)) 94369c415eSSeongJae Park goto free_out; 95369c415eSSeongJae Park 96369c415eSSeongJae Park /* 97369c415eSSeongJae Park * auto-tune sampling and aggregation interval aiming 4% DAMON-observed 98369c415eSSeongJae Park * accesses ratio, keeping sampling interval in [5ms, 10s] range. 99369c415eSSeongJae Park */ 100369c415eSSeongJae Park ctx->attrs.intervals_goal = (struct damon_intervals_goal) { 101369c415eSSeongJae Park .access_bp = 400, .aggrs = 3, 102369c415eSSeongJae Park .min_sample_us = 5000, .max_sample_us = 10000000, 103369c415eSSeongJae Park }; 104369c415eSSeongJae Park if (damon_select_ops(ctx, DAMON_OPS_PADDR)) 105369c415eSSeongJae Park goto free_out; 106369c415eSSeongJae Park 107369c415eSSeongJae Park target = damon_new_target(); 108369c415eSSeongJae Park if (!target) 109369c415eSSeongJae Park goto free_out; 110369c415eSSeongJae Park damon_add_target(ctx, target); 111369c415eSSeongJae Park if (damon_set_region_biggest_system_ram_default(target, &start, &end)) 112369c415eSSeongJae Park goto free_out; 113*fabdd1e9SSeongJae Park ctx->callback.after_aggregation = damon_stat_after_aggregation; 114369c415eSSeongJae Park return ctx; 115369c415eSSeongJae Park free_out: 116369c415eSSeongJae Park damon_destroy_ctx(ctx); 117369c415eSSeongJae Park return NULL; 118369c415eSSeongJae Park } 119369c415eSSeongJae Park 120369c415eSSeongJae Park static int damon_stat_start(void) 121369c415eSSeongJae Park { 122369c415eSSeongJae Park damon_stat_context = damon_stat_build_ctx(); 123369c415eSSeongJae Park if (!damon_stat_context) 124369c415eSSeongJae Park return -ENOMEM; 125369c415eSSeongJae Park return damon_start(&damon_stat_context, 1, true); 126369c415eSSeongJae Park } 127369c415eSSeongJae Park 128369c415eSSeongJae Park static void damon_stat_stop(void) 129369c415eSSeongJae Park { 130369c415eSSeongJae Park damon_stop(&damon_stat_context, 1); 131369c415eSSeongJae Park damon_destroy_ctx(damon_stat_context); 132369c415eSSeongJae Park } 133369c415eSSeongJae Park 134369c415eSSeongJae Park static bool damon_stat_init_called; 135369c415eSSeongJae Park 136369c415eSSeongJae Park static int damon_stat_enabled_store( 137369c415eSSeongJae Park const char *val, const struct kernel_param *kp) 138369c415eSSeongJae Park { 139369c415eSSeongJae Park bool is_enabled = enabled; 140369c415eSSeongJae Park int err; 141369c415eSSeongJae Park 142369c415eSSeongJae Park err = kstrtobool(val, &enabled); 143369c415eSSeongJae Park if (err) 144369c415eSSeongJae Park return err; 145369c415eSSeongJae Park 146369c415eSSeongJae Park if (is_enabled == enabled) 147369c415eSSeongJae Park return 0; 148369c415eSSeongJae Park 149369c415eSSeongJae Park if (!damon_stat_init_called) 150369c415eSSeongJae Park /* 151369c415eSSeongJae Park * probably called from command line parsing (parse_args()). 152369c415eSSeongJae Park * Cannot call damon_new_ctx(). Let damon_stat_init() handle. 153369c415eSSeongJae Park */ 154369c415eSSeongJae Park return 0; 155369c415eSSeongJae Park 156369c415eSSeongJae Park if (enabled) { 157369c415eSSeongJae Park err = damon_stat_start(); 158369c415eSSeongJae Park if (err) 159369c415eSSeongJae Park enabled = false; 160369c415eSSeongJae Park return err; 161369c415eSSeongJae Park } 162369c415eSSeongJae Park damon_stat_stop(); 163369c415eSSeongJae Park return 0; 164369c415eSSeongJae Park } 165369c415eSSeongJae Park 166369c415eSSeongJae Park static int __init damon_stat_init(void) 167369c415eSSeongJae Park { 168369c415eSSeongJae Park int err = 0; 169369c415eSSeongJae Park 170369c415eSSeongJae Park damon_stat_init_called = true; 171369c415eSSeongJae Park 172369c415eSSeongJae Park /* probably set via command line */ 173369c415eSSeongJae Park if (enabled) 174369c415eSSeongJae Park err = damon_stat_start(); 175369c415eSSeongJae Park 176369c415eSSeongJae Park if (err && enabled) 177369c415eSSeongJae Park enabled = false; 178369c415eSSeongJae Park return err; 179369c415eSSeongJae Park } 180369c415eSSeongJae Park 181369c415eSSeongJae Park module_init(damon_stat_init); 182