linux/mm/damon/dbgfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * DAMON Debugfs Interface
   4 *
   5 * Author: SeongJae Park <sjpark@amazon.de>
   6 */
   7
   8#define pr_fmt(fmt) "damon-dbgfs: " fmt
   9
  10#include <linux/damon.h>
  11#include <linux/debugfs.h>
  12#include <linux/file.h>
  13#include <linux/mm.h>
  14#include <linux/module.h>
  15#include <linux/page_idle.h>
  16#include <linux/slab.h>
  17
  18static struct damon_ctx **dbgfs_ctxs;
  19static int dbgfs_nr_ctxs;
  20static struct dentry **dbgfs_dirs;
  21static DEFINE_MUTEX(damon_dbgfs_lock);
  22
  23/*
  24 * Returns non-empty string on success, negative error code otherwise.
  25 */
  26static char *user_input_str(const char __user *buf, size_t count, loff_t *ppos)
  27{
  28        char *kbuf;
  29        ssize_t ret;
  30
  31        /* We do not accept continuous write */
  32        if (*ppos)
  33                return ERR_PTR(-EINVAL);
  34
  35        kbuf = kmalloc(count + 1, GFP_KERNEL);
  36        if (!kbuf)
  37                return ERR_PTR(-ENOMEM);
  38
  39        ret = simple_write_to_buffer(kbuf, count + 1, ppos, buf, count);
  40        if (ret != count) {
  41                kfree(kbuf);
  42                return ERR_PTR(-EIO);
  43        }
  44        kbuf[ret] = '\0';
  45
  46        return kbuf;
  47}
  48
  49static ssize_t dbgfs_attrs_read(struct file *file,
  50                char __user *buf, size_t count, loff_t *ppos)
  51{
  52        struct damon_ctx *ctx = file->private_data;
  53        char kbuf[128];
  54        int ret;
  55
  56        mutex_lock(&ctx->kdamond_lock);
  57        ret = scnprintf(kbuf, ARRAY_SIZE(kbuf), "%lu %lu %lu %lu %lu\n",
  58                        ctx->sample_interval, ctx->aggr_interval,
  59                        ctx->primitive_update_interval, ctx->min_nr_regions,
  60                        ctx->max_nr_regions);
  61        mutex_unlock(&ctx->kdamond_lock);
  62
  63        return simple_read_from_buffer(buf, count, ppos, kbuf, ret);
  64}
  65
  66static ssize_t dbgfs_attrs_write(struct file *file,
  67                const char __user *buf, size_t count, loff_t *ppos)
  68{
  69        struct damon_ctx *ctx = file->private_data;
  70        unsigned long s, a, r, minr, maxr;
  71        char *kbuf;
  72        ssize_t ret = count;
  73        int err;
  74
  75        kbuf = user_input_str(buf, count, ppos);
  76        if (IS_ERR(kbuf))
  77                return PTR_ERR(kbuf);
  78
  79        if (sscanf(kbuf, "%lu %lu %lu %lu %lu",
  80                                &s, &a, &r, &minr, &maxr) != 5) {
  81                ret = -EINVAL;
  82                goto out;
  83        }
  84
  85        mutex_lock(&ctx->kdamond_lock);
  86        if (ctx->kdamond) {
  87                ret = -EBUSY;
  88                goto unlock_out;
  89        }
  90
  91        err = damon_set_attrs(ctx, s, a, r, minr, maxr);
  92        if (err)
  93                ret = err;
  94unlock_out:
  95        mutex_unlock(&ctx->kdamond_lock);
  96out:
  97        kfree(kbuf);
  98        return ret;
  99}
 100
 101static inline bool targetid_is_pid(const struct damon_ctx *ctx)
 102{
 103        return ctx->primitive.target_valid == damon_va_target_valid;
 104}
 105
 106static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len)
 107{
 108        struct damon_target *t;
 109        unsigned long id;
 110        int written = 0;
 111        int rc;
 112
 113        damon_for_each_target(t, ctx) {
 114                id = t->id;
 115                if (targetid_is_pid(ctx))
 116                        /* Show pid numbers to debugfs users */
 117                        id = (unsigned long)pid_vnr((struct pid *)id);
 118
 119                rc = scnprintf(&buf[written], len - written, "%lu ", id);
 120                if (!rc)
 121                        return -ENOMEM;
 122                written += rc;
 123        }
 124        if (written)
 125                written -= 1;
 126        written += scnprintf(&buf[written], len - written, "\n");
 127        return written;
 128}
 129
 130static ssize_t dbgfs_target_ids_read(struct file *file,
 131                char __user *buf, size_t count, loff_t *ppos)
 132{
 133        struct damon_ctx *ctx = file->private_data;
 134        ssize_t len;
 135        char ids_buf[320];
 136
 137        mutex_lock(&ctx->kdamond_lock);
 138        len = sprint_target_ids(ctx, ids_buf, 320);
 139        mutex_unlock(&ctx->kdamond_lock);
 140        if (len < 0)
 141                return len;
 142
 143        return simple_read_from_buffer(buf, count, ppos, ids_buf, len);
 144}
 145
 146/*
 147 * Converts a string into an array of unsigned long integers
 148 *
 149 * Returns an array of unsigned long integers if the conversion success, or
 150 * NULL otherwise.
 151 */
 152static unsigned long *str_to_target_ids(const char *str, ssize_t len,
 153                                        ssize_t *nr_ids)
 154{
 155        unsigned long *ids;
 156        const int max_nr_ids = 32;
 157        unsigned long id;
 158        int pos = 0, parsed, ret;
 159
 160        *nr_ids = 0;
 161        ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL);
 162        if (!ids)
 163                return NULL;
 164        while (*nr_ids < max_nr_ids && pos < len) {
 165                ret = sscanf(&str[pos], "%lu%n", &id, &parsed);
 166                pos += parsed;
 167                if (ret != 1)
 168                        break;
 169                ids[*nr_ids] = id;
 170                *nr_ids += 1;
 171        }
 172
 173        return ids;
 174}
 175
 176static void dbgfs_put_pids(unsigned long *ids, int nr_ids)
 177{
 178        int i;
 179
 180        for (i = 0; i < nr_ids; i++)
 181                put_pid((struct pid *)ids[i]);
 182}
 183
 184static ssize_t dbgfs_target_ids_write(struct file *file,
 185                const char __user *buf, size_t count, loff_t *ppos)
 186{
 187        struct damon_ctx *ctx = file->private_data;
 188        char *kbuf, *nrs;
 189        unsigned long *targets;
 190        ssize_t nr_targets;
 191        ssize_t ret = count;
 192        int i;
 193        int err;
 194
 195        kbuf = user_input_str(buf, count, ppos);
 196        if (IS_ERR(kbuf))
 197                return PTR_ERR(kbuf);
 198
 199        nrs = kbuf;
 200
 201        targets = str_to_target_ids(nrs, ret, &nr_targets);
 202        if (!targets) {
 203                ret = -ENOMEM;
 204                goto out;
 205        }
 206
 207        if (targetid_is_pid(ctx)) {
 208                for (i = 0; i < nr_targets; i++) {
 209                        targets[i] = (unsigned long)find_get_pid(
 210                                        (int)targets[i]);
 211                        if (!targets[i]) {
 212                                dbgfs_put_pids(targets, i);
 213                                ret = -EINVAL;
 214                                goto free_targets_out;
 215                        }
 216                }
 217        }
 218
 219        mutex_lock(&ctx->kdamond_lock);
 220        if (ctx->kdamond) {
 221                if (targetid_is_pid(ctx))
 222                        dbgfs_put_pids(targets, nr_targets);
 223                ret = -EBUSY;
 224                goto unlock_out;
 225        }
 226
 227        err = damon_set_targets(ctx, targets, nr_targets);
 228        if (err) {
 229                if (targetid_is_pid(ctx))
 230                        dbgfs_put_pids(targets, nr_targets);
 231                ret = err;
 232        }
 233
 234unlock_out:
 235        mutex_unlock(&ctx->kdamond_lock);
 236free_targets_out:
 237        kfree(targets);
 238out:
 239        kfree(kbuf);
 240        return ret;
 241}
 242
 243static ssize_t dbgfs_kdamond_pid_read(struct file *file,
 244                char __user *buf, size_t count, loff_t *ppos)
 245{
 246        struct damon_ctx *ctx = file->private_data;
 247        char *kbuf;
 248        ssize_t len;
 249
 250        kbuf = kmalloc(count, GFP_KERNEL);
 251        if (!kbuf)
 252                return -ENOMEM;
 253
 254        mutex_lock(&ctx->kdamond_lock);
 255        if (ctx->kdamond)
 256                len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid);
 257        else
 258                len = scnprintf(kbuf, count, "none\n");
 259        mutex_unlock(&ctx->kdamond_lock);
 260        if (!len)
 261                goto out;
 262        len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
 263
 264out:
 265        kfree(kbuf);
 266        return len;
 267}
 268
 269static int damon_dbgfs_open(struct inode *inode, struct file *file)
 270{
 271        file->private_data = inode->i_private;
 272
 273        return nonseekable_open(inode, file);
 274}
 275
 276static const struct file_operations attrs_fops = {
 277        .open = damon_dbgfs_open,
 278        .read = dbgfs_attrs_read,
 279        .write = dbgfs_attrs_write,
 280};
 281
 282static const struct file_operations target_ids_fops = {
 283        .open = damon_dbgfs_open,
 284        .read = dbgfs_target_ids_read,
 285        .write = dbgfs_target_ids_write,
 286};
 287
 288static const struct file_operations kdamond_pid_fops = {
 289        .open = damon_dbgfs_open,
 290        .read = dbgfs_kdamond_pid_read,
 291};
 292
 293static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx)
 294{
 295        const char * const file_names[] = {"attrs", "target_ids",
 296                "kdamond_pid"};
 297        const struct file_operations *fops[] = {&attrs_fops, &target_ids_fops,
 298                &kdamond_pid_fops};
 299        int i;
 300
 301        for (i = 0; i < ARRAY_SIZE(file_names); i++)
 302                debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]);
 303}
 304
 305static int dbgfs_before_terminate(struct damon_ctx *ctx)
 306{
 307        struct damon_target *t, *next;
 308
 309        if (!targetid_is_pid(ctx))
 310                return 0;
 311
 312        damon_for_each_target_safe(t, next, ctx) {
 313                put_pid((struct pid *)t->id);
 314                damon_destroy_target(t);
 315        }
 316        return 0;
 317}
 318
 319static struct damon_ctx *dbgfs_new_ctx(void)
 320{
 321        struct damon_ctx *ctx;
 322
 323        ctx = damon_new_ctx();
 324        if (!ctx)
 325                return NULL;
 326
 327        damon_va_set_primitives(ctx);
 328        ctx->callback.before_terminate = dbgfs_before_terminate;
 329        return ctx;
 330}
 331
 332static void dbgfs_destroy_ctx(struct damon_ctx *ctx)
 333{
 334        damon_destroy_ctx(ctx);
 335}
 336
 337/*
 338 * Make a context of @name and create a debugfs directory for it.
 339 *
 340 * This function should be called while holding damon_dbgfs_lock.
 341 *
 342 * Returns 0 on success, negative error code otherwise.
 343 */
 344static int dbgfs_mk_context(char *name)
 345{
 346        struct dentry *root, **new_dirs, *new_dir;
 347        struct damon_ctx **new_ctxs, *new_ctx;
 348
 349        if (damon_nr_running_ctxs())
 350                return -EBUSY;
 351
 352        new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) *
 353                        (dbgfs_nr_ctxs + 1), GFP_KERNEL);
 354        if (!new_ctxs)
 355                return -ENOMEM;
 356        dbgfs_ctxs = new_ctxs;
 357
 358        new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) *
 359                        (dbgfs_nr_ctxs + 1), GFP_KERNEL);
 360        if (!new_dirs)
 361                return -ENOMEM;
 362        dbgfs_dirs = new_dirs;
 363
 364        root = dbgfs_dirs[0];
 365        if (!root)
 366                return -ENOENT;
 367
 368        new_dir = debugfs_create_dir(name, root);
 369        dbgfs_dirs[dbgfs_nr_ctxs] = new_dir;
 370
 371        new_ctx = dbgfs_new_ctx();
 372        if (!new_ctx) {
 373                debugfs_remove(new_dir);
 374                dbgfs_dirs[dbgfs_nr_ctxs] = NULL;
 375                return -ENOMEM;
 376        }
 377
 378        dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx;
 379        dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs],
 380                        dbgfs_ctxs[dbgfs_nr_ctxs]);
 381        dbgfs_nr_ctxs++;
 382
 383        return 0;
 384}
 385
 386static ssize_t dbgfs_mk_context_write(struct file *file,
 387                const char __user *buf, size_t count, loff_t *ppos)
 388{
 389        char *kbuf;
 390        char *ctx_name;
 391        ssize_t ret = count;
 392        int err;
 393
 394        kbuf = user_input_str(buf, count, ppos);
 395        if (IS_ERR(kbuf))
 396                return PTR_ERR(kbuf);
 397        ctx_name = kmalloc(count + 1, GFP_KERNEL);
 398        if (!ctx_name) {
 399                kfree(kbuf);
 400                return -ENOMEM;
 401        }
 402
 403        /* Trim white space */
 404        if (sscanf(kbuf, "%s", ctx_name) != 1) {
 405                ret = -EINVAL;
 406                goto out;
 407        }
 408
 409        mutex_lock(&damon_dbgfs_lock);
 410        err = dbgfs_mk_context(ctx_name);
 411        if (err)
 412                ret = err;
 413        mutex_unlock(&damon_dbgfs_lock);
 414
 415out:
 416        kfree(kbuf);
 417        kfree(ctx_name);
 418        return ret;
 419}
 420
 421/*
 422 * Remove a context of @name and its debugfs directory.
 423 *
 424 * This function should be called while holding damon_dbgfs_lock.
 425 *
 426 * Return 0 on success, negative error code otherwise.
 427 */
 428static int dbgfs_rm_context(char *name)
 429{
 430        struct dentry *root, *dir, **new_dirs;
 431        struct damon_ctx **new_ctxs;
 432        int i, j;
 433
 434        if (damon_nr_running_ctxs())
 435                return -EBUSY;
 436
 437        root = dbgfs_dirs[0];
 438        if (!root)
 439                return -ENOENT;
 440
 441        dir = debugfs_lookup(name, root);
 442        if (!dir)
 443                return -ENOENT;
 444
 445        new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs),
 446                        GFP_KERNEL);
 447        if (!new_dirs)
 448                return -ENOMEM;
 449
 450        new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs),
 451                        GFP_KERNEL);
 452        if (!new_ctxs) {
 453                kfree(new_dirs);
 454                return -ENOMEM;
 455        }
 456
 457        for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) {
 458                if (dbgfs_dirs[i] == dir) {
 459                        debugfs_remove(dbgfs_dirs[i]);
 460                        dbgfs_destroy_ctx(dbgfs_ctxs[i]);
 461                        continue;
 462                }
 463                new_dirs[j] = dbgfs_dirs[i];
 464                new_ctxs[j++] = dbgfs_ctxs[i];
 465        }
 466
 467        kfree(dbgfs_dirs);
 468        kfree(dbgfs_ctxs);
 469
 470        dbgfs_dirs = new_dirs;
 471        dbgfs_ctxs = new_ctxs;
 472        dbgfs_nr_ctxs--;
 473
 474        return 0;
 475}
 476
 477static ssize_t dbgfs_rm_context_write(struct file *file,
 478                const char __user *buf, size_t count, loff_t *ppos)
 479{
 480        char *kbuf;
 481        ssize_t ret = count;
 482        int err;
 483        char *ctx_name;
 484
 485        kbuf = user_input_str(buf, count, ppos);
 486        if (IS_ERR(kbuf))
 487                return PTR_ERR(kbuf);
 488        ctx_name = kmalloc(count + 1, GFP_KERNEL);
 489        if (!ctx_name) {
 490                kfree(kbuf);
 491                return -ENOMEM;
 492        }
 493
 494        /* Trim white space */
 495        if (sscanf(kbuf, "%s", ctx_name) != 1) {
 496                ret = -EINVAL;
 497                goto out;
 498        }
 499
 500        mutex_lock(&damon_dbgfs_lock);
 501        err = dbgfs_rm_context(ctx_name);
 502        if (err)
 503                ret = err;
 504        mutex_unlock(&damon_dbgfs_lock);
 505
 506out:
 507        kfree(kbuf);
 508        kfree(ctx_name);
 509        return ret;
 510}
 511
 512static ssize_t dbgfs_monitor_on_read(struct file *file,
 513                char __user *buf, size_t count, loff_t *ppos)
 514{
 515        char monitor_on_buf[5];
 516        bool monitor_on = damon_nr_running_ctxs() != 0;
 517        int len;
 518
 519        len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n");
 520
 521        return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len);
 522}
 523
 524static ssize_t dbgfs_monitor_on_write(struct file *file,
 525                const char __user *buf, size_t count, loff_t *ppos)
 526{
 527        ssize_t ret = count;
 528        char *kbuf;
 529        int err;
 530
 531        kbuf = user_input_str(buf, count, ppos);
 532        if (IS_ERR(kbuf))
 533                return PTR_ERR(kbuf);
 534
 535        /* Remove white space */
 536        if (sscanf(kbuf, "%s", kbuf) != 1) {
 537                kfree(kbuf);
 538                return -EINVAL;
 539        }
 540
 541        if (!strncmp(kbuf, "on", count))
 542                err = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs);
 543        else if (!strncmp(kbuf, "off", count))
 544                err = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs);
 545        else
 546                err = -EINVAL;
 547
 548        if (err)
 549                ret = err;
 550        kfree(kbuf);
 551        return ret;
 552}
 553
 554static const struct file_operations mk_contexts_fops = {
 555        .write = dbgfs_mk_context_write,
 556};
 557
 558static const struct file_operations rm_contexts_fops = {
 559        .write = dbgfs_rm_context_write,
 560};
 561
 562static const struct file_operations monitor_on_fops = {
 563        .read = dbgfs_monitor_on_read,
 564        .write = dbgfs_monitor_on_write,
 565};
 566
 567static int __init __damon_dbgfs_init(void)
 568{
 569        struct dentry *dbgfs_root;
 570        const char * const file_names[] = {"mk_contexts", "rm_contexts",
 571                "monitor_on"};
 572        const struct file_operations *fops[] = {&mk_contexts_fops,
 573                &rm_contexts_fops, &monitor_on_fops};
 574        int i;
 575
 576        dbgfs_root = debugfs_create_dir("damon", NULL);
 577
 578        for (i = 0; i < ARRAY_SIZE(file_names); i++)
 579                debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL,
 580                                fops[i]);
 581        dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]);
 582
 583        dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL);
 584        if (!dbgfs_dirs) {
 585                debugfs_remove(dbgfs_root);
 586                return -ENOMEM;
 587        }
 588        dbgfs_dirs[0] = dbgfs_root;
 589
 590        return 0;
 591}
 592
 593/*
 594 * Functions for the initialization
 595 */
 596
 597static int __init damon_dbgfs_init(void)
 598{
 599        int rc;
 600
 601        dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL);
 602        if (!dbgfs_ctxs)
 603                return -ENOMEM;
 604        dbgfs_ctxs[0] = dbgfs_new_ctx();
 605        if (!dbgfs_ctxs[0]) {
 606                kfree(dbgfs_ctxs);
 607                return -ENOMEM;
 608        }
 609        dbgfs_nr_ctxs = 1;
 610
 611        rc = __damon_dbgfs_init();
 612        if (rc) {
 613                kfree(dbgfs_ctxs[0]);
 614                kfree(dbgfs_ctxs);
 615                pr_err("%s: dbgfs init failed\n", __func__);
 616        }
 617
 618        return rc;
 619}
 620
 621module_init(damon_dbgfs_init);
 622
 623#include "dbgfs-test.h"
 624