1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Shows data access monitoring results in simple metrics. 4 */ 5 6 #define pr_fmt(fmt) "damon-stat: " fmt 7 8 #include <linux/damon.h> 9 #include <linux/init.h> 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/sort.h> 13 14 #ifdef MODULE_PARAM_PREFIX 15 #undef MODULE_PARAM_PREFIX 16 #endif 17 #define MODULE_PARAM_PREFIX "damon_stat." 18 19 static int damon_stat_enabled_store( 20 const char *val, const struct kernel_param *kp); 21 22 static int damon_stat_enabled_load(char *buffer, 23 const struct kernel_param *kp); 24 25 static const struct kernel_param_ops enabled_param_ops = { 26 .set = damon_stat_enabled_store, 27 .get = damon_stat_enabled_load, 28 }; 29 30 static bool enabled __read_mostly = IS_ENABLED( 31 CONFIG_DAMON_STAT_ENABLED_DEFAULT); 32 module_param_cb(enabled, &enabled_param_ops, NULL, 0600); 33 MODULE_PARM_DESC(enabled, "Enable of disable DAMON_STAT"); 34 35 static unsigned long estimated_memory_bandwidth __read_mostly; 36 module_param(estimated_memory_bandwidth, ulong, 0400); 37 MODULE_PARM_DESC(estimated_memory_bandwidth, 38 "Estimated memory bandwidth usage in bytes per second"); 39 40 static long memory_idle_ms_percentiles[101] = {0,}; 41 module_param_array(memory_idle_ms_percentiles, long, NULL, 0400); 42 MODULE_PARM_DESC(memory_idle_ms_percentiles, 43 "Memory idle time percentiles in milliseconds"); 44 45 static unsigned long aggr_interval_us; 46 module_param(aggr_interval_us, ulong, 0400); 47 MODULE_PARM_DESC(aggr_interval_us, 48 "Current tuned aggregation interval in microseconds"); 49 50 static struct damon_ctx *damon_stat_context; 51 52 static unsigned long damon_stat_last_refresh_jiffies; 53 54 static void damon_stat_set_estimated_memory_bandwidth(struct damon_ctx *c) 55 { 56 struct damon_target *t; 57 struct damon_region *r; 58 unsigned long access_bytes = 0; 59 60 damon_for_each_target(t, c) { 61 damon_for_each_region(r, t) 62 access_bytes += (r->ar.end - r->ar.start) * 63 r->nr_accesses; 64 } 65 estimated_memory_bandwidth = access_bytes * USEC_PER_MSEC * 66 MSEC_PER_SEC / c->attrs.aggr_interval; 67 } 68 69 static int damon_stat_idletime(const struct damon_region *r) 70 { 71 if (r->nr_accesses) 72 return -1 * (r->age + 1); 73 return r->age + 1; 74 } 75 76 static int damon_stat_cmp_regions(const void *a, const void *b) 77 { 78 const struct damon_region *ra = *(const struct damon_region **)a; 79 const struct damon_region *rb = *(const struct damon_region **)b; 80 81 return damon_stat_idletime(ra) - damon_stat_idletime(rb); 82 } 83 84 static int damon_stat_sort_regions(struct damon_ctx *c, 85 struct damon_region ***sorted_ptr, int *nr_regions_ptr, 86 unsigned long *total_sz_ptr) 87 { 88 struct damon_target *t; 89 struct damon_region *r; 90 struct damon_region **region_pointers; 91 unsigned int nr_regions = 0; 92 unsigned long total_sz = 0; 93 94 damon_for_each_target(t, c) { 95 /* there is only one target */ 96 region_pointers = kmalloc_objs(*region_pointers, 97 damon_nr_regions(t)); 98 if (!region_pointers) 99 return -ENOMEM; 100 damon_for_each_region(r, t) { 101 region_pointers[nr_regions++] = r; 102 total_sz += r->ar.end - r->ar.start; 103 } 104 } 105 sort(region_pointers, nr_regions, sizeof(*region_pointers), 106 damon_stat_cmp_regions, NULL); 107 *sorted_ptr = region_pointers; 108 *nr_regions_ptr = nr_regions; 109 *total_sz_ptr = total_sz; 110 return 0; 111 } 112 113 static void damon_stat_set_idletime_percentiles(struct damon_ctx *c) 114 { 115 struct damon_region **sorted_regions, *region; 116 int nr_regions; 117 unsigned long total_sz, accounted_bytes = 0; 118 int err, i, next_percentile = 0; 119 120 err = damon_stat_sort_regions(c, &sorted_regions, &nr_regions, 121 &total_sz); 122 if (err) 123 return; 124 for (i = 0; i < nr_regions; i++) { 125 region = sorted_regions[i]; 126 accounted_bytes += region->ar.end - region->ar.start; 127 while (next_percentile <= accounted_bytes * 100 / total_sz) 128 memory_idle_ms_percentiles[next_percentile++] = 129 damon_stat_idletime(region) * 130 (long)c->attrs.aggr_interval / USEC_PER_MSEC; 131 } 132 kfree(sorted_regions); 133 } 134 135 static int damon_stat_damon_call_fn(void *data) 136 { 137 struct damon_ctx *c = data; 138 139 /* avoid unnecessarily frequent stat update */ 140 if (time_before_eq(jiffies, damon_stat_last_refresh_jiffies + 141 msecs_to_jiffies(5 * MSEC_PER_SEC))) 142 return 0; 143 damon_stat_last_refresh_jiffies = jiffies; 144 145 aggr_interval_us = c->attrs.aggr_interval; 146 damon_stat_set_estimated_memory_bandwidth(c); 147 damon_stat_set_idletime_percentiles(c); 148 return 0; 149 } 150 151 static struct damon_ctx *damon_stat_build_ctx(void) 152 { 153 struct damon_ctx *ctx; 154 struct damon_attrs attrs; 155 struct damon_target *target; 156 unsigned long start = 0, end = 0; 157 158 ctx = damon_new_ctx(); 159 if (!ctx) 160 return NULL; 161 attrs = (struct damon_attrs) { 162 .sample_interval = 5 * USEC_PER_MSEC, 163 .aggr_interval = 100 * USEC_PER_MSEC, 164 .ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC, 165 .min_nr_regions = 10, 166 .max_nr_regions = 1000, 167 }; 168 /* 169 * auto-tune sampling and aggregation interval aiming 4% DAMON-observed 170 * accesses ratio, keeping sampling interval in [5ms, 10s] range. 171 */ 172 attrs.intervals_goal = (struct damon_intervals_goal) { 173 .access_bp = 400, .aggrs = 3, 174 .min_sample_us = 5000, .max_sample_us = 10000000, 175 }; 176 if (damon_set_attrs(ctx, &attrs)) 177 goto free_out; 178 179 if (damon_select_ops(ctx, DAMON_OPS_PADDR)) 180 goto free_out; 181 182 target = damon_new_target(); 183 if (!target) 184 goto free_out; 185 damon_add_target(ctx, target); 186 if (damon_set_region_system_rams_default(target, &start, &end, 187 ctx->addr_unit, ctx->min_region_sz)) 188 goto free_out; 189 return ctx; 190 free_out: 191 damon_destroy_ctx(ctx); 192 return NULL; 193 } 194 195 static struct damon_call_control call_control = { 196 .fn = damon_stat_damon_call_fn, 197 .repeat = true, 198 }; 199 200 static int damon_stat_start(void) 201 { 202 int err; 203 204 if (damon_stat_context) { 205 if (damon_is_running(damon_stat_context)) 206 return -EAGAIN; 207 damon_destroy_ctx(damon_stat_context); 208 } 209 210 damon_stat_context = damon_stat_build_ctx(); 211 if (!damon_stat_context) 212 return -ENOMEM; 213 err = damon_start(&damon_stat_context, 1, true); 214 if (err) { 215 damon_destroy_ctx(damon_stat_context); 216 damon_stat_context = NULL; 217 return err; 218 } 219 220 damon_stat_last_refresh_jiffies = jiffies; 221 call_control.data = damon_stat_context; 222 return damon_call(damon_stat_context, &call_control); 223 } 224 225 static void damon_stat_stop(void) 226 { 227 damon_stop(&damon_stat_context, 1); 228 damon_destroy_ctx(damon_stat_context); 229 damon_stat_context = NULL; 230 } 231 232 static bool damon_stat_enabled(void) 233 { 234 if (!damon_stat_context) 235 return false; 236 return damon_is_running(damon_stat_context); 237 } 238 239 static int damon_stat_enabled_store( 240 const char *val, const struct kernel_param *kp) 241 { 242 int err; 243 244 err = kstrtobool(val, &enabled); 245 if (err) 246 return err; 247 248 if (damon_stat_enabled() == enabled) 249 return 0; 250 251 if (!damon_initialized()) 252 /* 253 * probably called from command line parsing (parse_args()). 254 * Cannot call damon_new_ctx(). Let damon_stat_init() handle. 255 */ 256 return 0; 257 258 if (enabled) 259 return damon_stat_start(); 260 damon_stat_stop(); 261 return 0; 262 } 263 264 static int damon_stat_enabled_load(char *buffer, const struct kernel_param *kp) 265 { 266 return sprintf(buffer, "%c\n", damon_stat_enabled() ? 'Y' : 'N'); 267 } 268 269 static int damon_stat_kdamond_pid_store( 270 const char *val, const struct kernel_param *kp) 271 { 272 /* 273 * kdamond_pid is read-only, but kernel command line could write it. 274 * Do nothing here. 275 */ 276 return 0; 277 } 278 279 static int damon_stat_kdamond_pid_load( 280 char *buffer, const struct kernel_param *kp) 281 { 282 int pid; 283 284 if (!damon_stat_context) { 285 pid = -1; 286 } else { 287 pid = damon_kdamond_pid(damon_stat_context); 288 if (pid < 1) 289 pid = -1; 290 } 291 return sprintf(buffer, "%d\n", pid); 292 } 293 294 static const struct kernel_param_ops kdamond_pid_param_ops = { 295 .set = damon_stat_kdamond_pid_store, 296 .get = damon_stat_kdamond_pid_load, 297 }; 298 299 /* 300 * PID of the DAMON thread 301 * 302 * If DAMON_STAT is enabled, this becomes the PID of the worker thread. 303 * Else, -1. 304 */ 305 module_param_cb(kdamond_pid, &kdamond_pid_param_ops, NULL, 0400); 306 MODULE_PARM_DESC(kdamond_pid, "pid of the kdamond"); 307 308 static int __init damon_stat_init(void) 309 { 310 int err = 0; 311 312 if (!damon_initialized()) { 313 err = -ENOMEM; 314 goto out; 315 } 316 317 /* probably set via command line */ 318 if (enabled) 319 err = damon_stat_start(); 320 321 out: 322 if (err && enabled) 323 enabled = false; 324 return err; 325 } 326 327 module_init(damon_stat_init); 328