From patchwork Thu Feb 17 16:19:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: SeongJae Park X-Patchwork-Id: 12750412 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1506EC433EF for ; Thu, 17 Feb 2022 16:19:50 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 5CC766B0074; Thu, 17 Feb 2022 11:19:49 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 554EC6B0078; Thu, 17 Feb 2022 11:19:49 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 3ABA76B007B; Thu, 17 Feb 2022 11:19:49 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (relay.hostedemail.com [64.99.140.26]) by kanga.kvack.org (Postfix) with ESMTP id 1BDC96B0074 for ; Thu, 17 Feb 2022 11:19:49 -0500 (EST) Received: from smtpin03.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay12.hostedemail.com (Postfix) with ESMTP id DC011120249 for ; Thu, 17 Feb 2022 16:19:48 +0000 (UTC) X-FDA: 79152782856.03.2FF3BBE Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by imf05.hostedemail.com (Postfix) with ESMTP id 2C35D100002 for ; Thu, 17 Feb 2022 16:19:47 +0000 (UTC) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 3EDC4611DE; Thu, 17 Feb 2022 16:19:47 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id F1A88C340EF; Thu, 17 Feb 2022 16:19:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1645114786; bh=nSqATp1Okb9kyoRb4k6ZZfys2TemnjaMTSvROPMwD2U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SwYfuVTBYG66dOXn/JQYGulvLKlz18T2XNcQQmSXNJ8kwb7xKPFwbJSu1n8phefgv sktxsJsbNEWOIymTSS4IHCBMR91wGdUYi10+WUEXXTxnZtvTAPsFCEpgiosU5Yjyw5 8qiCHNAXyVWRfFQZlWDgpBYwaCUtSJciJQAfVWEOF5Kuso0cmXZjpNNc4G0MFkFtUo rXPlCOdTi5ncUhfZKAZK927rLgXZBwIxC8nvRd9WIt7yd3vFjW3GwNPjUWJfoU7no/ T0i4sSE8KsAXLY+aBEQgfMUJixsKHdSxE+CMpEQx1GfPtqytLweMakX1w0TNqAXjEt 6TbN0s6kTyTNg== From: SeongJae Park To: akpm@linux-foundation.org Cc: rientjes@google.com, xhao@linux.alibaba.com, linux-damon@amazon.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, SeongJae Park Subject: [RFC PATCH 1/4] mm/damon: Implement a sysfs-based DAMON user interface Date: Thu, 17 Feb 2022 16:19:35 +0000 Message-Id: <20220217161938.8874-2-sj@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220217161938.8874-1-sj@kernel.org> References: <20220217161938.8874-1-sj@kernel.org> MIME-Version: 1.0 X-Rspamd-Server: rspam12 X-Rspamd-Queue-Id: 2C35D100002 X-Stat-Signature: dq1gyzcyjpeqydnisz18o8ri95qjakwq X-Rspam-User: Authentication-Results: imf05.hostedemail.com; dkim=pass header.d=kernel.org header.s=k20201202 header.b=SwYfuVTB; spf=pass (imf05.hostedemail.com: domain of sj@kernel.org designates 139.178.84.217 as permitted sender) smtp.mailfrom=sj@kernel.org; dmarc=pass (policy=none) header.from=kernel.org X-HE-Tag: 1645114787-808800 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: DAMON's debugfs-based user interface served very well, so far. However, it unnecessarily depends on debugfs, while DAMON is not aimed to be used for only debugging. Also, the interface receives multiple values via one file. For example, schemes file receives 18 values. As a result, it is not only hard to be used, but also difficult to be extended. Especially, keeping backward compatibility of user space tools is getting only challenging. It would be better to implement another reliable and flexible interface and deprecate the debugfs interface in long term. For the reason, this commit implements a stub of a part of the sysfs-based new user interface of DAMON. Specifically, this commit implements only the sysfs control part for virtual address space monitoring. Commits for linking with DAMON and support of more features that debugfs is currently providing will follow. The idea of the new interface is, using directory hierarchies and making one file for one value. For a short example, users can do the virtual address monitoring via the interface as below: # cd /sys/kernel/mm/damon/admin/ # echo 1 > kdamonds/nr # echo 1 > kdamonds/0/contexts/nr # echo vaddr > kdamonds/0/contexts/0/damon_type # echo 1 > kdamonds/0/contexts/0/targets/nr # echo $(pidof ) > kdamonds/0/contexts/0/targets/0/pid # echo on > kdamonds/0/state Main difference between DAMON_DBGFS and DAMON_SYSFS --------------------------------------------------- DAMON debugfs interface allows multiple monitoring contexts, but it asks users to turn those all on and off at once. It's not a big problem but makes the operation a little bit complex. DAMON_SYSFS allows users to turn on and off monitoring contexts individually. The Hierarchy ------------- In a glance, the files hierarchy of the sysfs interface will look like below. /sys/kernel/mm/damon/admin │ kdamonds │ │ nr │ │ 0/ │ │ │ state,pid │ │ │ contexts │ │ │ │ nr │ │ │ │ 0/ │ │ │ │ │ damon_type │ │ │ │ │ monitoring_attrs/ │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us │ │ │ │ │ │ nr_regions/min,max │ │ │ │ │ targets/ │ │ │ │ │ │ nr │ │ │ │ │ │ 0/ │ │ │ │ │ │ │ pid │ │ │ │ │ │ │ regions/ │ │ │ │ │ │ │ │ nr │ │ │ │ │ │ │ │ 0/ │ │ │ │ │ │ │ │ │ start,end │ │ │ │ │ │ │ │ ... │ │ │ │ │ │ ... │ │ │ │ │ ... │ │ ... Root ---- The root of the DAMON sysfs is /sys/kernel/mm/damon/, and it has one directory named 'admin'. The directory contains the interface for privileged user space programs. User space tools or deamons having root permission could use this directory. In a future, sibling directories for non-root user space tools or deamons (we could allow them to do monitong of their own virtual address space) or control of in-kernel DAMON-based deamons could be created. kdamonds/ --------- The monitoring-related information including request specifications and results are called DAMON context. DAMON executes a set of the contexts with a kernel thread called kdamond (for now, only one context per kdamond is supported), and multiple kdamonds could run in parallel. This directory has files for controlling the kdamonds. In the beginning, this directory has only one file, 'nr'. Writing a number (`N`) to the file creates the number of child directories named `0` to `N-1`. Each directory represents each kdamond. kdamonds// ------------- In each kdamond directory, two files (`state` and `pid`) and one directory (`contexts`) reside. Reading `state` returns `on` if the kdamond is currently running, or `off` if it is not running. Writing `on` or `off` makes the kdamond be in the state. If the state is `on`, reading `pid` shows the pid of the kdamond thread. `contexts` directory resembles `kdamonds`. It contains files for controlling the monitoring contexts that this kdamond will execute. kdamonds//contexts/ ---------------------- In the beginning, this directory has only one file, 'nr'. Writing a number (`N`) to the file creates the number of child directories named as `0` to `N-1`. Each directory represents each monitoring context. At the moment, only one context per kdamond is supported, so only `0` or `1` can be written to the file. contexts// ------------- In each context directory, one file (`operations`) and two directories (`monitoring_attrs` and `targets`) reside. DAMON supports multiple types of monitoring operations, including those for virtual address space and physical address space. You can set and show what type of monitoring operations you want to use with the context by writing one of below keywords to, and reading the file. - vaddr: Monitor virtual address spaces of specific processes Files for specifying attributes of the monitoring including required quality and efficiency of the monitoring are in `monitoring_attrs` directory, while files for specifying to what memory regions the monitoring should be done are in `targets` directory. contexts//monitoring_attrs/ ------------------------------ In this directory, you can show two directories, `intervals` and `nr_regions`. Under `intervals` directory, three files for DAMON's sampling interval (`sample_us`), aggregation interval (`aggr_us`) and update interval (`update_us`) exist. You can set and get the values by writing to and reading from the files. Under `nr_regions` directory, two files for the lower-bound and upper-bound of DAMON's monitoring regions (`min` and `max`, respectively), which controls the monitoring overhead, reside. You can set and get the values by writing to and rading from the files. For more details about the intervals and monitoring regions range, please read the Design document[1]. [1] https://docs.kernel.org/vm/damon/design.html contexts//targets/ --------------------- In the beginning, this directory has only one file, 'nr'. Writing a number (`N`) to the file creates the number of child directories named `0` to `N-1`. Each directory represents each monitoring target. targets// ------------ In each target directory, one file (`pid`) exists. You can make the context to monitor the virtual address space of a process by writing the pid of the process to the file, and show what process's virtual address space the context is set to monitor by reading the file. Signed-off-by: SeongJae Park --- mm/damon/Kconfig | 7 + mm/damon/Makefile | 1 + mm/damon/sysfs.c | 1100 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1108 insertions(+) create mode 100644 mm/damon/sysfs.c diff --git a/mm/damon/Kconfig b/mm/damon/Kconfig index 01bad77ad7ae..9b559c76d6dd 100644 --- a/mm/damon/Kconfig +++ b/mm/damon/Kconfig @@ -52,6 +52,13 @@ config DAMON_VADDR_KUNIT_TEST If unsure, say N. +config DAMON_SYSFS + bool "DAMON sysfs interface" + depends on DAMON && SYSFS + help + This builds the sysfs interface for DAMON. The user space can use + the interface for arbitrary data access monitoring. + config DAMON_DBGFS bool "DAMON debugfs interface" depends on DAMON_VADDR && DAMON_PADDR && DEBUG_FS diff --git a/mm/damon/Makefile b/mm/damon/Makefile index 03931472991a..d7e316ab9eb7 100644 --- a/mm/damon/Makefile +++ b/mm/damon/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_DAMON) := core.o obj-$(CONFIG_DAMON_VADDR) += ops-common.o vaddr.o obj-$(CONFIG_DAMON_PADDR) += ops-common.o paddr.o obj-$(CONFIG_DAMON_DBGFS) += dbgfs.o +obj-$(CONFIG_DAMON_SYSFS) += sysfs.o obj-$(CONFIG_DAMON_RECLAIM) += reclaim.o diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c new file mode 100644 index 000000000000..59bdc7268dc6 --- /dev/null +++ b/mm/damon/sysfs.c @@ -0,0 +1,1100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DAMON sysfs Interface + * + * Copyright (c) 2022 SeongJae Park + */ + +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(damon_sysfs_lock); + +/* + * unsigned long range directory + */ + +struct damon_sysfs_ul_range { + struct kobject kobj; + unsigned long min; + unsigned long max; +}; + +static struct damon_sysfs_ul_range *damon_sysfs_ul_range_alloc( + unsigned long min, + unsigned long max) +{ + struct damon_sysfs_ul_range *range = kmalloc(sizeof(*range), + GFP_KERNEL); + + if (!range) + return NULL; + range->kobj = (struct kobject){}; + range->min = min; + range->max = max; + + return range; +} + +static ssize_t damon_sysfs_ul_range_min_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_ul_range *range = container_of(kobj, + struct damon_sysfs_ul_range, kobj); + + return sysfs_emit(buf, "%lu\n", range->min); +} + +static ssize_t damon_sysfs_ul_range_min_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_ul_range *range = container_of(kobj, + struct damon_sysfs_ul_range, kobj); + unsigned long min; + int err; + + err = kstrtoul(buf, 0, &min); + if (err) + return -EINVAL; + + range->min = min; + return count; +} + +static ssize_t damon_sysfs_ul_range_max_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_ul_range *range = container_of(kobj, + struct damon_sysfs_ul_range, kobj); + + return sysfs_emit(buf, "%lu\n", range->max); +} + +static ssize_t damon_sysfs_ul_range_max_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_ul_range *range = container_of(kobj, + struct damon_sysfs_ul_range, kobj); + unsigned long max; + int err; + + err = kstrtoul(buf, 0, &max); + if (err) + return -EINVAL; + + range->max = max; + return count; +} + +static void damon_sysfs_ul_range_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_ul_range, kobj)); +} + +static struct kobj_attribute damon_sysfs_ul_range_min_attr = + __ATTR(min, 0600, damon_sysfs_ul_range_min_show, + damon_sysfs_ul_range_min_store); + +static struct kobj_attribute damon_sysfs_ul_range_max_attr = + __ATTR(max, 0600, damon_sysfs_ul_range_max_show, + damon_sysfs_ul_range_max_store); + +static struct attribute *damon_sysfs_ul_range_attrs[] = { + &damon_sysfs_ul_range_min_attr.attr, + &damon_sysfs_ul_range_max_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_ul_range); + +static struct kobj_type damon_sysfs_ul_range_ktype = { + .release = damon_sysfs_ul_range_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_ul_range_groups, +}; + +/* + * target directory + */ + +struct damon_sysfs_target { + struct kobject kobj; + int pid; +}; + +static struct damon_sysfs_target *damon_sysfs_target_alloc(void) +{ + return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL); +} + +static ssize_t damon_sysfs_target_pid_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_target *target = container_of(kobj, + struct damon_sysfs_target, kobj); + + return sysfs_emit(buf, "%d\n", target->pid); +} + +static ssize_t damon_sysfs_target_pid_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_target *target = container_of(kobj, + struct damon_sysfs_target, kobj); + int err = kstrtoint(buf, 0, &target->pid); + + if (err) + return -EINVAL; + return count; +} + +static void damon_sysfs_target_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_target, kobj)); +} + +static struct kobj_attribute damon_sysfs_target_pid_attr = __ATTR(pid, 0600, + damon_sysfs_target_pid_show, damon_sysfs_target_pid_store); + +static struct attribute *damon_sysfs_target_attrs[] = { + &damon_sysfs_target_pid_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_target); + +static struct kobj_type damon_sysfs_target_ktype = { + .release = damon_sysfs_target_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_target_groups, +}; + +/* + * targets directory + */ + +struct damon_sysfs_targets { + struct kobject kobj; + struct damon_sysfs_target **targets_arr; + int nr_targets; +}; + +static struct damon_sysfs_targets *damon_sysfs_targets_alloc(void) +{ + return kzalloc(sizeof(struct damon_sysfs_targets), GFP_KERNEL); +} + +static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets) +{ + struct damon_sysfs_target **targets_arr = targets->targets_arr; + int i; + + for (i = 0; i < targets->nr_targets; i++) + kobject_put(&targets_arr[i]->kobj); + kfree(targets_arr); + targets->targets_arr = NULL; + targets->nr_targets = 0; +} + +static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets, + int nr_targets) +{ + struct damon_sysfs_target **targets_arr, *target; + int err, i; + + damon_sysfs_targets_rm_dirs(targets); + if (!nr_targets) + return 0; + + targets_arr = kmalloc_array(nr_targets, sizeof(*targets_arr), + GFP_KERNEL); + if (!targets_arr) + return -ENOMEM; + targets->targets_arr = targets_arr; + + for (i = 0; i < nr_targets; i++) { + target = damon_sysfs_target_alloc(); + if (!target) { + damon_sysfs_targets_rm_dirs(targets); + return -ENOMEM; + } + + err = kobject_init_and_add(&target->kobj, + &damon_sysfs_target_ktype, &targets->kobj, + "%d", i); + if (err) { + kfree(target); + damon_sysfs_targets_rm_dirs(targets); + return err; + } + + targets_arr[i] = target; + targets->nr_targets++; + } + return 0; +} + +static ssize_t damon_sysfs_targets_nr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_targets *targets = container_of(kobj, + struct damon_sysfs_targets, kobj); + + return sysfs_emit(buf, "%d\n", targets->nr_targets); +} + +static ssize_t damon_sysfs_targets_nr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_targets *targets = container_of(kobj, + struct damon_sysfs_targets, kobj); + int nr; + int err = kstrtoint(buf, 0, &nr); + + if (err) + return err; + if (nr < 0) + return -EINVAL; + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + err = damon_sysfs_targets_add_dirs(targets, nr); + mutex_unlock(&damon_sysfs_lock); + if (err) + return err; + + return count; +} + +static void damon_sysfs_targets_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_targets, kobj)); +} + +static struct kobj_attribute damon_sysfs_targets_nr_attr = __ATTR(nr, 0600, + damon_sysfs_targets_nr_show, damon_sysfs_targets_nr_store); + +static struct attribute *damon_sysfs_targets_attrs[] = { + &damon_sysfs_targets_nr_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_targets); + +static struct kobj_type damon_sysfs_targets_ktype = { + .release = damon_sysfs_targets_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_targets_groups, +}; + +/* + * intervals directory + */ + +struct damon_sysfs_intervals { + struct kobject kobj; + unsigned long sample_us; + unsigned long aggr_us; + unsigned long update_us; +}; + +static struct damon_sysfs_intervals *damon_sysfs_intervals_alloc( + unsigned long sample_us, unsigned long aggr_us, + unsigned long update_us) +{ + struct damon_sysfs_intervals *intervals = kmalloc(sizeof(*intervals), + GFP_KERNEL); + + if (!intervals) + return NULL; + + intervals->kobj = (struct kobject){}; + intervals->sample_us = sample_us; + intervals->aggr_us = aggr_us; + intervals->update_us = update_us; + return intervals; +} + +static ssize_t damon_sysfs_intervals_sample_us_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_intervals *intervals = container_of(kobj, + struct damon_sysfs_intervals, kobj); + + return sysfs_emit(buf, "%lu\n", intervals->sample_us); +} + +static ssize_t damon_sysfs_intervals_sample_us_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_intervals *intervals = container_of(kobj, + struct damon_sysfs_intervals, kobj); + unsigned long us; + int err = kstrtoul(buf, 0, &us); + + if (err) + return -EINVAL; + + intervals->sample_us = us; + return count; +} + +static ssize_t damon_sysfs_intervals_aggr_us_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_intervals *intervals = container_of(kobj, + struct damon_sysfs_intervals, kobj); + + return sysfs_emit(buf, "%lu\n", intervals->aggr_us); +} + +static ssize_t damon_sysfs_intervals_aggr_us_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_intervals *intervals = container_of(kobj, + struct damon_sysfs_intervals, kobj); + unsigned long us; + int err = kstrtoul(buf, 0, &us); + + if (err) + return -EINVAL; + + intervals->aggr_us = us; + return count; +} + +static ssize_t damon_sysfs_intervals_update_us_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_intervals *intervals = container_of(kobj, + struct damon_sysfs_intervals, kobj); + + return sysfs_emit(buf, "%lu\n", intervals->update_us); +} + +static ssize_t damon_sysfs_intervals_update_us_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_intervals *intervals = container_of(kobj, + struct damon_sysfs_intervals, kobj); + unsigned long us; + int err = kstrtoul(buf, 0, &us); + + if (err) + return -EINVAL; + + intervals->update_us = us; + return count; +} + +static void damon_sysfs_intervals_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_intervals, kobj)); +} + +static struct kobj_attribute damon_sysfs_intervals_sample_us_attr = + __ATTR(sample_us, 0600, + damon_sysfs_intervals_sample_us_show, + damon_sysfs_intervals_sample_us_store); + +static struct kobj_attribute damon_sysfs_intervals_aggr_us_attr = + __ATTR(aggr_us, 0600, + damon_sysfs_intervals_aggr_us_show, + damon_sysfs_intervals_aggr_us_store); + +static struct kobj_attribute damon_sysfs_intervals_update_us_attr = + __ATTR(update_us, 0600, + damon_sysfs_intervals_update_us_show, + damon_sysfs_intervals_update_us_store); + +static struct attribute *damon_sysfs_intervals_attrs[] = { + &damon_sysfs_intervals_sample_us_attr.attr, + &damon_sysfs_intervals_aggr_us_attr.attr, + &damon_sysfs_intervals_update_us_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_intervals); + +static struct kobj_type damon_sysfs_intervals_ktype = { + .release = damon_sysfs_intervals_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_intervals_groups, +}; + +/* + * monitoring_attrs directory + */ + +struct damon_sysfs_attrs { + struct kobject kobj; + struct damon_sysfs_intervals *intervals; + struct damon_sysfs_ul_range *nr_regions; +}; + +static struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void) +{ + struct damon_sysfs_attrs *attrs = kmalloc(sizeof(*attrs), GFP_KERNEL); + + if (!attrs) + return NULL; + attrs->kobj = (struct kobject){}; + return attrs; +} + +static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs) +{ + struct damon_sysfs_intervals *intervals; + struct damon_sysfs_ul_range *nr_regions_range; + int err; + + intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000); + if (!intervals) + return -ENOMEM; + + err = kobject_init_and_add(&intervals->kobj, + &damon_sysfs_intervals_ktype, &attrs->kobj, + "intervals"); + if (err) { + kfree(intervals); + return err; + } + attrs->intervals = intervals; + + nr_regions_range = damon_sysfs_ul_range_alloc(10, 1000); + if (!nr_regions_range) + return -ENOMEM; + + err = kobject_init_and_add(&nr_regions_range->kobj, + &damon_sysfs_ul_range_ktype, &attrs->kobj, + "nr_regions"); + if (err) { + kfree(nr_regions_range); + kobject_put(&intervals->kobj); + return err; + } + attrs->nr_regions = nr_regions_range; + + return 0; +} + +static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs) +{ + kobject_put(&attrs->nr_regions->kobj); + kobject_put(&attrs->intervals->kobj); +} + +static void damon_sysfs_attrs_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_attrs, kobj)); +} + +static struct attribute *damon_sysfs_attrs_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_attrs); + +static struct kobj_type damon_sysfs_attrs_ktype = { + .release = damon_sysfs_attrs_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_attrs_groups, +}; + +/* + * context directory + */ + +static const char * const damon_sysfs_ops_strs[] = { + "vaddr\n", + "paddr\n", +}; + +struct damon_sysfs_context { + struct kobject kobj; + enum damon_ops_id ops_id; + struct damon_sysfs_attrs *attrs; + struct damon_sysfs_targets *targets; +}; + +static struct damon_sysfs_context *damon_sysfs_context_alloc( + enum damon_ops_id ops_id) +{ + struct damon_sysfs_context *context = kmalloc(sizeof(*context), + GFP_KERNEL); + + if (!context) + return NULL; + context->kobj = (struct kobject){}; + context->ops_id = ops_id; + return context; +} + +static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context) +{ + struct damon_sysfs_attrs *attrs; + struct damon_sysfs_targets *targets; + int err; + + /* add monitoring_attrs directory */ + attrs = damon_sysfs_attrs_alloc(); + if (!attrs) + return -ENOMEM; + err = kobject_init_and_add(&attrs->kobj, &damon_sysfs_attrs_ktype, + &context->kobj, "monitoring_attrs"); + if (err) { + kfree(attrs); + return err; + } + err = damon_sysfs_attrs_add_dirs(attrs); + if (err) + goto put_attrs_out; + + /* add targets directory */ + targets = damon_sysfs_targets_alloc(); + if (!targets) { + err = -ENOMEM; + goto put_attrs_out; + } + err = kobject_init_and_add(&targets->kobj, &damon_sysfs_targets_ktype, + &context->kobj, "targets"); + if (err) { + kfree(targets); + goto put_attrs_out; + } + + context->attrs = attrs; + context->targets = targets; + return err; + +put_attrs_out: + kobject_put(&attrs->kobj); + return err; +} + +static void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context) +{ + damon_sysfs_attrs_rm_dirs(context->attrs); + kobject_put(&context->attrs->kobj); + damon_sysfs_targets_rm_dirs(context->targets); + kobject_put(&context->targets->kobj); +} + +static ssize_t damon_sysfs_context_operations_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_context *context = container_of(kobj, + struct damon_sysfs_context, kobj); + + return sysfs_emit(buf, "%s\n", damon_sysfs_ops_strs[context->ops_id]); +} + +static ssize_t damon_sysfs_context_operations_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_context *context = container_of(kobj, + struct damon_sysfs_context, kobj); + + if (!strncmp(buf, damon_sysfs_ops_strs[DAMON_OPS_VADDR], count)) + context->ops_id = DAMON_OPS_VADDR; + else if (!strncmp(buf, damon_sysfs_ops_strs[DAMON_OPS_PADDR], count)) + context->ops_id = DAMON_OPS_PADDR; + else + return -EINVAL; + + return count; +} + +static void damon_sysfs_context_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_context, kobj)); +} + +static struct kobj_attribute damon_sysfs_context_operations_attr = __ATTR( + operations, 0600, damon_sysfs_context_operations_show, + damon_sysfs_context_operations_store); + +static struct attribute *damon_sysfs_context_attrs[] = { + &damon_sysfs_context_operations_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_context); + +static struct kobj_type damon_sysfs_context_ktype = { + .release = damon_sysfs_context_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_context_groups, +}; + +/* + * contexts directory + */ + +struct damon_sysfs_contexts { + struct kobject kobj; + struct damon_sysfs_context **contexts_arr; + int nr; +}; + +static struct damon_sysfs_contexts *damon_sysfs_contexts_alloc(void) +{ + return kzalloc(sizeof(struct damon_sysfs_contexts), GFP_KERNEL); +} + +static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts) +{ + struct damon_sysfs_context **contexts_arr = contexts->contexts_arr; + int i; + + for (i = 0; i < contexts->nr; i++) { + damon_sysfs_context_rm_dirs(contexts_arr[i]); + kobject_put(&contexts_arr[i]->kobj); + } + kfree(contexts_arr); + contexts->contexts_arr = NULL; + contexts->nr = 0; +} + +static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts, + int nr_contexts) +{ + struct damon_sysfs_context **contexts_arr, *context; + int err, i; + + damon_sysfs_contexts_rm_dirs(contexts); + if (!nr_contexts) + return 0; + + contexts_arr = kmalloc_array(nr_contexts, sizeof(*contexts_arr), + GFP_KERNEL); + if (!contexts_arr) + return -ENOMEM; + contexts->contexts_arr = contexts_arr; + + for (i = 0; i < nr_contexts; i++) { + context = damon_sysfs_context_alloc(DAMON_OPS_VADDR); + if (!context) { + damon_sysfs_contexts_rm_dirs(contexts); + return -ENOMEM; + } + + err = kobject_init_and_add(&context->kobj, + &damon_sysfs_context_ktype, &contexts->kobj, + "%d", i); + if (err) { + kfree(context); + damon_sysfs_contexts_rm_dirs(contexts); + return err; + } + + err = damon_sysfs_context_add_dirs(context); + if (err) { + kobject_put(&context->kobj); + damon_sysfs_contexts_rm_dirs(contexts); + return err; + } + + contexts_arr[i] = context; + contexts->nr++; + } + return 0; +} + +static ssize_t damon_sysfs_contexts_nr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_contexts *contexts = container_of(kobj, + struct damon_sysfs_contexts, kobj); + + return sysfs_emit(buf, "%d\n", contexts->nr); +} + +static ssize_t damon_sysfs_contexts_nr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_contexts *contexts = container_of(kobj, + struct damon_sysfs_contexts, kobj); + int nr, err; + + err = kstrtoint(buf, 0, &nr); + if (err) + return err; + if (nr < 0 || 1 < nr) + return -EINVAL; + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + err = damon_sysfs_contexts_add_dirs(contexts, nr); + mutex_unlock(&damon_sysfs_lock); + if (err) + return err; + + return count; +} + +static void damon_sysfs_contexts_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_contexts, kobj)); +} + +static struct kobj_attribute damon_sysfs_contexts_nr_attr = __ATTR(nr, 0600, + damon_sysfs_contexts_nr_show, damon_sysfs_contexts_nr_store); + +static struct attribute *damon_sysfs_contexts_attrs[] = { + &damon_sysfs_contexts_nr_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_contexts); + +static struct kobj_type damon_sysfs_contexts_ktype = { + .release = damon_sysfs_contexts_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_contexts_groups, +}; + +/* + * kdamond directory + */ + +struct damon_sysfs_kdamond { + struct kobject kobj; + struct damon_sysfs_contexts *contexts; + int pid; + struct damon_ctx *damon_ctx; +}; + +static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void) +{ + return kzalloc(sizeof(struct damon_sysfs_kdamond), GFP_KERNEL); +} + +static int damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond *kdamond) +{ + struct damon_sysfs_contexts *contexts; + int err; + + contexts = damon_sysfs_contexts_alloc(); + if (!contexts) + return -ENOMEM; + + err = kobject_init_and_add(&contexts->kobj, + &damon_sysfs_contexts_ktype, &kdamond->kobj, + "contexts"); + if (err) { + kfree(contexts); + return err; + } + kdamond->contexts = contexts; + + return err; +} + +static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond) +{ + damon_sysfs_contexts_rm_dirs(kdamond->contexts); + kobject_put(&kdamond->contexts->kobj); +} + +static ssize_t damon_sysfs_kdamond_state_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + /* TODO: Link with DAMON */ + return sysfs_emit(buf, "off\n"); +} + +static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + /* TODO: Link with DAMON */ + return count; +} + +static ssize_t damon_sysfs_kdamond_pid_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_kdamond *kdamond = container_of(kobj, + struct damon_sysfs_kdamond, kobj); + struct damon_ctx *ctx; + int pid; + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + ctx = kdamond->damon_ctx; + if (!ctx) { + pid = -1; + goto out; + } + mutex_lock(&ctx->kdamond_lock); + if (!ctx->kdamond) { + pid = -1; + goto unlock_kdamond_lock_out; + } + pid = ctx->kdamond->pid; + +unlock_kdamond_lock_out: + mutex_unlock(&ctx->kdamond_lock); +out: + mutex_unlock(&damon_sysfs_lock); + return sysfs_emit(buf, "%d\n", pid); +} + +static void damon_sysfs_kdamond_release(struct kobject *kobj) +{ + struct damon_sysfs_kdamond *kdamond = container_of(kobj, + struct damon_sysfs_kdamond, kobj); + + if (kdamond->damon_ctx) + damon_destroy_ctx(kdamond->damon_ctx); + kfree(container_of(kobj, struct damon_sysfs_kdamond, kobj)); +} + +static struct kobj_attribute damon_sysfs_kdamond_state_attr = + __ATTR(state, 0600, damon_sysfs_kdamond_state_show, + damon_sysfs_kdamond_state_store); + +static struct kobj_attribute damon_sysfs_kdamond_pid_attr = __ATTR(pid, 0400, + damon_sysfs_kdamond_pid_show, NULL); + +static struct attribute *damon_sysfs_kdamond_attrs[] = { + &damon_sysfs_kdamond_state_attr.attr, + &damon_sysfs_kdamond_pid_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_kdamond); + +static struct kobj_type damon_sysfs_kdamond_ktype = { + .release = damon_sysfs_kdamond_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_kdamond_groups, +}; + +/* + * kdamonds directory + */ + +struct damon_sysfs_kdamonds { + struct kobject kobj; + struct damon_sysfs_kdamond **kdamonds_arr; + int nr; +}; + +static struct damon_sysfs_kdamonds *damon_sysfs_kdamonds_alloc(void) +{ + return kzalloc(sizeof(struct damon_sysfs_kdamonds), GFP_KERNEL); +} + +static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds) +{ + struct damon_sysfs_kdamond **kdamonds_arr = kdamonds->kdamonds_arr; + int i; + + for (i = 0; i < kdamonds->nr; i++) { + damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]); + kobject_put(&kdamonds_arr[i]->kobj); + } + kfree(kdamonds_arr); + kdamonds->kdamonds_arr = NULL; + kdamonds->nr = 0; +} + +int damon_sysfs_nr_running_ctxs(struct damon_sysfs_kdamond **kdamonds, + int nr_kdamonds) +{ + int nr_running_ctxs = 0; + int i; + + for (i = 0; i < nr_kdamonds; i++) { + struct damon_ctx *ctx; + + ctx = kdamonds[i]->damon_ctx; + if (!ctx) + continue; + mutex_lock(&ctx->kdamond_lock); + if (ctx->kdamond) + nr_running_ctxs++; + mutex_unlock(&ctx->kdamond_lock); + } + return nr_running_ctxs; +} + +static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds, + int nr_kdamonds) +{ + struct damon_sysfs_kdamond **kdamonds_arr, *kdamond; + int err, i; + + if (damon_sysfs_nr_running_ctxs(kdamonds->kdamonds_arr, kdamonds->nr)) + return -EBUSY; + + damon_sysfs_kdamonds_rm_dirs(kdamonds); + if (!nr_kdamonds) + return 0; + + kdamonds_arr = kmalloc_array(nr_kdamonds, sizeof(*kdamonds_arr), + GFP_KERNEL); + if (!kdamonds_arr) + return -ENOMEM; + kdamonds->kdamonds_arr = kdamonds_arr; + + for (i = 0; i < nr_kdamonds; i++) { + kdamond = damon_sysfs_kdamond_alloc(); + if (!kdamond) { + damon_sysfs_kdamonds_rm_dirs(kdamonds); + return -ENOMEM; + } + + err = kobject_init_and_add(&kdamond->kobj, + &damon_sysfs_kdamond_ktype, &kdamonds->kobj, + "%d", i); + if (err) { + damon_sysfs_kdamonds_rm_dirs(kdamonds); + kfree(kdamond); + return err; + } + + err = damon_sysfs_kdamond_add_dirs(kdamond); + if (err) { + kobject_put(&kdamond->kobj); + damon_sysfs_kdamonds_rm_dirs(kdamonds); + return err; + } + + kdamonds_arr[i] = kdamond; + kdamonds->nr++; + } + return 0; +} + +static ssize_t damon_sysfs_kdamonds_nr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct damon_sysfs_kdamonds *kdamonds = container_of(kobj, + struct damon_sysfs_kdamonds, kobj); + + return sysfs_emit(buf, "%d\n", kdamonds->nr); +} + +static ssize_t damon_sysfs_kdamonds_nr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct damon_sysfs_kdamonds *kdamonds = container_of(kobj, + struct damon_sysfs_kdamonds, kobj); + int nr, err; + + err = kstrtoint(buf, 0, &nr); + if (err) + return err; + if (nr < 0) + return -EINVAL; + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + err = damon_sysfs_kdamonds_add_dirs(kdamonds, nr); + mutex_unlock(&damon_sysfs_lock); + if (err) + return err; + + return count; +} + +static void damon_sysfs_kdamonds_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_kdamonds, kobj)); +} + +static struct kobj_attribute damon_sysfs_kdamonds_nr_attr = __ATTR(nr, 0600, + damon_sysfs_kdamonds_nr_show, damon_sysfs_kdamonds_nr_store); + +static struct attribute *damon_sysfs_kdamonds_attrs[] = { + &damon_sysfs_kdamonds_nr_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_kdamonds); + +static struct kobj_type damon_sysfs_kdamonds_ktype = { + .release = damon_sysfs_kdamonds_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_kdamonds_groups, +}; + +/* + * damon user interface directory + */ + +struct damon_sysfs_ui_dir { + struct kobject kobj; + struct damon_sysfs_kdamonds *kdamonds; +}; + +static struct damon_sysfs_ui_dir *damon_sysfs_ui_dir_alloc(void) +{ + return kzalloc(sizeof(struct damon_sysfs_ui_dir), GFP_KERNEL); +} + +static int damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir *ui_dir) +{ + struct damon_sysfs_kdamonds *kdamonds; + int err; + + kdamonds = damon_sysfs_kdamonds_alloc(); + if (!kdamonds) + return -ENOMEM; + + err = kobject_init_and_add(&kdamonds->kobj, + &damon_sysfs_kdamonds_ktype, &ui_dir->kobj, + "kdamonds"); + if (err) { + kfree(kdamonds); + return err; + } + ui_dir->kdamonds = kdamonds; + return err; +} + +static void damon_sysfs_ui_dir_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct damon_sysfs_ui_dir, kobj)); +} + +static struct attribute *damon_sysfs_ui_dir_attrs[] = { + NULL, +}; +ATTRIBUTE_GROUPS(damon_sysfs_ui_dir); + +static struct kobj_type damon_sysfs_ui_dir_ktype = { + .release = damon_sysfs_ui_dir_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = damon_sysfs_ui_dir_groups, +}; + +static int __init damon_sysfs_init(void) +{ + struct kobject *damon_sysfs_root; + struct damon_sysfs_ui_dir *admin; + int err; + + damon_sysfs_root = kobject_create_and_add("damon", mm_kobj); + if (!damon_sysfs_root) + return -ENOMEM; + + admin = damon_sysfs_ui_dir_alloc(); + if (!admin) { + kobject_put(damon_sysfs_root); + return -ENOMEM; + } + err = kobject_init_and_add(&admin->kobj, &damon_sysfs_ui_dir_ktype, + damon_sysfs_root, "admin"); + if (err) { + kobject_put(&admin->kobj); + kobject_put(damon_sysfs_root); + return err; + } + + err = damon_sysfs_ui_dir_add_dirs(admin); + if (err) { + kobject_put(&admin->kobj); + kobject_put(damon_sysfs_root); + return err; + } + + return 0; +} +subsys_initcall(damon_sysfs_init); From patchwork Thu Feb 17 16:19:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: SeongJae Park X-Patchwork-Id: 12750411 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B338C433F5 for ; Thu, 17 Feb 2022 16:19:51 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 860D06B0075; Thu, 17 Feb 2022 11:19:49 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 706556B007D; Thu, 17 Feb 2022 11:19:49 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 4BA9A6B0075; Thu, 17 Feb 2022 11:19:49 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0048.hostedemail.com [216.40.44.48]) by kanga.kvack.org (Postfix) with ESMTP id 3223D6B0078 for ; Thu, 17 Feb 2022 11:19:49 -0500 (EST) Received: from smtpin19.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay03.hostedemail.com (Postfix) with ESMTP id E05538249980 for ; Thu, 17 Feb 2022 16:19:48 +0000 (UTC) X-FDA: 79152782856.19.8295EF6 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by imf08.hostedemail.com (Postfix) with ESMTP id 566C4160005 for ; Thu, 17 Feb 2022 16:19:48 +0000 (UTC) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id A318A60B96; Thu, 17 Feb 2022 16:19:47 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id E0A67C340EC; Thu, 17 Feb 2022 16:19:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1645114787; bh=P6kUoPILhYhj1H1ifaqC4pebGTsEc9wuH7gl6nlprR0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V1o9wlMo51/L2Ub2in/MXvW7QvA5HncVdVd9OnlEDQPukvom+zickh53AgQj7UPXE L79GqSD0lEoH9wJ6l5DL8q8mrrO/j3pUm1xuGhIrmn7Brhu7l5nB2EkZzNtmurxvC6 PYPDC3oGWXHZmd9+slxhFJ174enwgLZs7Bq9RVigY/LlDqADZ8zpfPZnXA0jDVEh4J r45nLVx+keh8Ss8PRQuHC7vOgD4W4KdmEzHJqu8/Nq9gUqbrXYtUgi1XBpa9dCEwvP wna1wZ211U6q72OcpV7n+x0Ve7dLU9ya+PjJYX0snfsapeAAdGn6rISb00KDcfUQbY tlb8DP3cTxOVg== From: SeongJae Park To: akpm@linux-foundation.org Cc: rientjes@google.com, xhao@linux.alibaba.com, linux-damon@amazon.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, SeongJae Park Subject: [RFC PATCH 2/4] mm/damon/core: Allow non-exclusive DAMON start/stop Date: Thu, 17 Feb 2022 16:19:36 +0000 Message-Id: <20220217161938.8874-3-sj@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220217161938.8874-1-sj@kernel.org> References: <20220217161938.8874-1-sj@kernel.org> X-Rspamd-Server: rspam01 X-Rspamd-Queue-Id: 566C4160005 X-Stat-Signature: c6umnwuigga3zrpdgqyn9cwnd7zzt4cr X-Rspam-User: Authentication-Results: imf08.hostedemail.com; dkim=pass header.d=kernel.org header.s=k20201202 header.b=V1o9wlMo; dmarc=pass (policy=none) header.from=kernel.org; spf=pass (imf08.hostedemail.com: domain of sj@kernel.org designates 139.178.84.217 as permitted sender) smtp.mailfrom=sj@kernel.org X-HE-Tag: 1645114788-57593 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: To avoid interference between DAMON users, current start/stop functions of DAMON allows only exclusive groups. This makes use of DAMON a little bit restrictive. Meanwhile, admins could somehow aware each DAMON usage and therefore could address the interference on their own. This commit hence allows callers of the functions to specify if the contexts should run in an exclusive mode or not. Signed-off-by: SeongJae Park --- include/linux/damon.h | 2 +- mm/damon/core.c | 22 ++++++++++++++++------ mm/damon/dbgfs.c | 2 +- mm/damon/reclaim.c | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 49c4a11ecf20..f8e99e47d747 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -508,7 +508,7 @@ int damon_nr_running_ctxs(void); int damon_register_ops(struct damon_operations *ops); int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id); -int damon_start(struct damon_ctx **ctxs, int nr_ctxs); +int damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive); int damon_stop(struct damon_ctx **ctxs, int nr_ctxs); #endif /* CONFIG_DAMON */ diff --git a/mm/damon/core.c b/mm/damon/core.c index 82e0a4620c4f..08c5e4dd2ed9 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -24,6 +24,7 @@ static DEFINE_MUTEX(damon_lock); static int nr_running_ctxs; +static bool running_exclusive_ctxs; static DEFINE_MUTEX(damon_ops_lock); static struct damon_operations damon_registered_ops[NR_DAMON_OPS]; @@ -434,22 +435,25 @@ static int __damon_start(struct damon_ctx *ctx) * damon_start() - Starts the monitorings for a given group of contexts. * @ctxs: an array of the pointers for contexts to start monitoring * @nr_ctxs: size of @ctxs + * @exclusive: exclusiveness of this contexts group * * This function starts a group of monitoring threads for a group of monitoring * contexts. One thread per each context is created and run in parallel. The - * caller should handle synchronization between the threads by itself. If a - * group of threads that created by other 'damon_start()' call is currently - * running, this function does nothing but returns -EBUSY. + * caller should handle synchronization between the threads by itself. If + * @exclusive is true and a group of threads that created by other + * 'damon_start()' call is currently running, this function does nothing but + * returns -EBUSY. * * Return: 0 on success, negative error code otherwise. */ -int damon_start(struct damon_ctx **ctxs, int nr_ctxs) +int damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive) { int i; int err = 0; mutex_lock(&damon_lock); - if (nr_running_ctxs) { + if ((exclusive && nr_running_ctxs) || + (!exclusive && running_exclusive_ctxs)) { mutex_unlock(&damon_lock); return -EBUSY; } @@ -460,13 +464,15 @@ int damon_start(struct damon_ctx **ctxs, int nr_ctxs) break; nr_running_ctxs++; } + if (exclusive && nr_running_ctxs) + running_exclusive_ctxs = true; mutex_unlock(&damon_lock); return err; } /* - * __damon_stop() - Stops monitoring of given context. + * __damon_stop() - Stops monitoring of a given context. * @ctx: monitoring context * * Return: 0 on success, negative error code otherwise. @@ -506,6 +512,10 @@ int damon_stop(struct damon_ctx **ctxs, int nr_ctxs) if (err) return err; } + mutex_lock(&damon_lock); + if (!nr_running_ctxs && running_exclusive_ctxs) + running_exclusive_ctxs = false; + mutex_unlock(&damon_lock); return err; } diff --git a/mm/damon/dbgfs.c b/mm/damon/dbgfs.c index 05b574cbcea8..a0dab8b5e45f 100644 --- a/mm/damon/dbgfs.c +++ b/mm/damon/dbgfs.c @@ -967,7 +967,7 @@ static ssize_t dbgfs_monitor_on_write(struct file *file, return -EINVAL; } } - ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs); + ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs, true); } else if (!strncmp(kbuf, "off", count)) { ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); } else { diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index b53d9c22fad1..e34c4d0c4d93 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -330,7 +330,7 @@ static int damon_reclaim_turn(bool on) if (err) goto free_scheme_out; - err = damon_start(&ctx, 1); + err = damon_start(&ctx, 1, true); if (!err) { kdamond_pid = ctx->kdamond->pid; return 0; From patchwork Thu Feb 17 16:19:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: SeongJae Park X-Patchwork-Id: 12750414 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id DEF04C433F5 for ; Thu, 17 Feb 2022 16:19:54 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 50B546B007D; Thu, 17 Feb 2022 11:19:50 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 442166B007B; Thu, 17 Feb 2022 11:19:50 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 112856B007D; Thu, 17 Feb 2022 11:19:50 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0223.hostedemail.com [216.40.44.223]) by kanga.kvack.org (Postfix) with ESMTP id ED2436B007B for ; Thu, 17 Feb 2022 11:19:49 -0500 (EST) Received: from smtpin22.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay04.hostedemail.com (Postfix) with ESMTP id B3F5092762 for ; Thu, 17 Feb 2022 16:19:49 +0000 (UTC) X-FDA: 79152782898.22.D1DA55B Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by imf19.hostedemail.com (Postfix) with ESMTP id 499001A0008 for ; Thu, 17 Feb 2022 16:19:49 +0000 (UTC) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 783C261210; Thu, 17 Feb 2022 16:19:48 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id B20C3C340F3; Thu, 17 Feb 2022 16:19:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1645114788; bh=86/d0xi9LNikqugSFOyNSfFep2UlHDk2g/2oy5k4oEQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eEk1KP0P1Ru7nw3eTQhYjXWvpF2DTvf7W1pMSZs/zqUJGjp0e/VnEskIvolzcBV4T tNqpoWqrdv97UM7fZR5E4nbWaBA/Dums9OYKbqHzsjsRUIm0u7+plWVOFSK/8kyjm2 xikQexBGTgbH1868dxj3O5B958yvTEW1YhBlPyeqNLSPP+02SvrteDRL9Nxf2fFVDl q1v+mWARrsMnqJxqWHsVydvX5RvPIJ6/ChqQmRhjMFXSd5La+y9aQk5O/ApcTH0ufG CN+cMWTM0aWjP+mDh0CohnqWCO9HxlsYK/QJ2NXIU4p49FBuNtHYq3mmGZ4HrS90nt CvdTI/uiVFp5Q== From: SeongJae Park To: akpm@linux-foundation.org Cc: rientjes@google.com, xhao@linux.alibaba.com, linux-damon@amazon.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, SeongJae Park Subject: [RFC PATCH 3/4] mm/damon/sysfs: Link DAMON to 'state' file read/write functions Date: Thu, 17 Feb 2022 16:19:37 +0000 Message-Id: <20220217161938.8874-4-sj@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220217161938.8874-1-sj@kernel.org> References: <20220217161938.8874-1-sj@kernel.org> Authentication-Results: imf19.hostedemail.com; dkim=pass header.d=kernel.org header.s=k20201202 header.b=eEk1KP0P; spf=pass (imf19.hostedemail.com: domain of sj@kernel.org designates 139.178.84.217 as permitted sender) smtp.mailfrom=sj@kernel.org; dmarc=pass (policy=none) header.from=kernel.org X-Rspamd-Server: rspam07 X-Rspam-User: X-Rspamd-Queue-Id: 499001A0008 X-Stat-Signature: qryueeo3q1ampjq6wojt6hqrx68zjn7m X-HE-Tag: 1645114789-660688 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: This commit implements 'state' DAMON sysfs interface file read/write functions. In detail, writing 'on' or 'off' to the 'state' file turns DAMON for the context on or off, accordingly. Reading the file shows the state. Signed-off-by: SeongJae Park --- mm/damon/sysfs.c | 166 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 4 deletions(-) diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index 59bdc7268dc6..721ee086265f 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -793,18 +793,176 @@ static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond) kobject_put(&kdamond->contexts->kobj); } +static bool damon_sysfs_ctx_running(struct damon_ctx *ctx) +{ + bool running; + + mutex_lock(&ctx->kdamond_lock); + running = ctx->kdamond != NULL; + mutex_unlock(&ctx->kdamond_lock); + return running; +} + static ssize_t damon_sysfs_kdamond_state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - /* TODO: Link with DAMON */ - return sysfs_emit(buf, "off\n"); + struct damon_sysfs_kdamond *kdamond = container_of(kobj, + struct damon_sysfs_kdamond, kobj); + struct damon_ctx *ctx = kdamond->damon_ctx; + bool running; + + if (!ctx) + running = false; + else + running = damon_sysfs_ctx_running(ctx); + + return sysfs_emit(buf, "%s\n", running ? "on" : "off"); +} + +static void damon_sysfs_destroy_targets(struct damon_ctx *ctx) +{ + struct damon_target *t, *next; + + damon_for_each_target_safe(t, next, ctx) { + if (ctx->ops.id == DAMON_OPS_VADDR) + put_pid(t->pid); + damon_destroy_target(t); + } +} + +static int damon_sysfs_set_targets(struct damon_ctx *ctx, + struct damon_sysfs_context *sysfs_ctx) +{ + struct damon_sysfs_targets *targets = sysfs_ctx->targets; + int i; + + for (i = 0; i < targets->nr_targets; i++) { + struct damon_target *t; + + t = damon_new_target(); + if (!t) { + damon_sysfs_destroy_targets(ctx); + return -ENOMEM; + } + if (ctx->ops.id == DAMON_OPS_VADDR) { + t->pid = find_get_pid(targets->targets_arr[i]->pid); + if (!t->pid) { + damon_sysfs_destroy_targets(ctx); + return -EINVAL; + } + } + damon_add_target(ctx, t); + } + return 0; +} + +static inline bool target_has_pid(const struct damon_ctx *ctx) +{ + return ctx->ops.id == DAMON_OPS_VADDR; +} + +static void damon_sysfs_before_terminate(struct damon_ctx *ctx) +{ + struct damon_target *t, *next; + + if (!target_has_pid(ctx)) + return; + + mutex_lock(&ctx->kdamond_lock); + damon_for_each_target_safe(t, next, ctx) { + put_pid(t->pid); + damon_destroy_target(t); + } + mutex_unlock(&ctx->kdamond_lock); +} + +static struct damon_ctx *damon_sysfs_build_ctx( + struct damon_sysfs_context *sys_ctx) +{ + struct damon_ctx *ctx = damon_new_ctx(); + struct damon_sysfs_attrs *sys_attrs = sys_ctx->attrs; + struct damon_sysfs_ul_range *sys_nr_regions = sys_attrs->nr_regions; + struct damon_sysfs_intervals *sys_intervals = sys_attrs->intervals; + int err; + + if (!ctx) + return ERR_PTR(-ENOMEM); + + err = damon_select_ops(ctx, sys_ctx->ops_id); + if (err) + goto out; + + err = damon_set_attrs(ctx, sys_intervals->sample_us, + sys_intervals->aggr_us, sys_intervals->update_us, + sys_nr_regions->min, sys_nr_regions->max); + if (err) + goto out; + err = damon_sysfs_set_targets(ctx, sys_ctx); + if (err) + goto out; + ctx->callback.before_terminate = damon_sysfs_before_terminate; + return ctx; + +out: + damon_destroy_ctx(ctx); + return ERR_PTR(err); } static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - /* TODO: Link with DAMON */ - return count; + struct damon_sysfs_kdamond *kdamond = container_of(kobj, + struct damon_sysfs_kdamond, kobj); + struct damon_ctx *ctx; + ssize_t ret; + + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; + if (!strncmp(buf, "on\n", count)) { + if (kdamond->damon_ctx && + damon_sysfs_ctx_running(kdamond->damon_ctx)) { + ret = -EBUSY; + goto out; + } + if (kdamond->contexts->nr != 1) { + ret = -EINVAL; + goto out; + } + + if (kdamond->damon_ctx) + damon_destroy_ctx(kdamond->damon_ctx); + kdamond->damon_ctx = NULL; + + ctx = damon_sysfs_build_ctx( + kdamond->contexts->contexts_arr[0]); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto out; + } + ret = damon_start(&ctx, 1, false); + if (ret) { + damon_destroy_ctx(ctx); + goto out; + } + kdamond->damon_ctx = ctx; + } else if (!strncmp(buf, "off\n", count)) { + if (!kdamond->damon_ctx) { + ret = -EINVAL; + goto out; + } + ret = damon_stop(&kdamond->damon_ctx, 1); + /* + * kdamond->damon_ctx will be freed in next on, or + * kdamonds_nr_store() + */ + } else { + ret = -EINVAL; + } +out: + mutex_unlock(&damon_sysfs_lock); + if (!ret) + ret = count; + return ret; } static ssize_t damon_sysfs_kdamond_pid_show(struct kobject *kobj, From patchwork Thu Feb 17 16:19:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: SeongJae Park X-Patchwork-Id: 12750415 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D386C4332F for ; Thu, 17 Feb 2022 16:19:56 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 83E156B007B; Thu, 17 Feb 2022 11:19:51 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 7C7656B007E; Thu, 17 Feb 2022 11:19:51 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 5F3996B0080; Thu, 17 Feb 2022 11:19:51 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0164.hostedemail.com [216.40.44.164]) by kanga.kvack.org (Postfix) with ESMTP id 3CA0F6B007B for ; Thu, 17 Feb 2022 11:19:51 -0500 (EST) Received: from smtpin11.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay02.hostedemail.com (Postfix) with ESMTP id F406C813BC for ; Thu, 17 Feb 2022 16:19:50 +0000 (UTC) X-FDA: 79152782982.11.E99908F Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by imf29.hostedemail.com (Postfix) with ESMTP id 56540120002 for ; Thu, 17 Feb 2022 16:19:50 +0000 (UTC) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id AD30060FF0; Thu, 17 Feb 2022 16:19:49 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 87A2DC340E9; Thu, 17 Feb 2022 16:19:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1645114789; bh=ByqICwRC78hF+OVa1i8FtpczDlPGoJKesouvh34GZ3o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RB2Y70pWaGFKMD4kiFjKstoMd3D8Nm3shVBsYlu4kPBzGuFPUjabi7ZFiQpK4NYQJ JPVrWTPBPpdqJSklmW8CwxhQM5WcLNWj6qMGR+o38B59T2YGLWw9TdicHrWJJqBpB2 4C4yS0ULi1K9J9saRRUoaUHHTYh/O0m1Y8Jcnr0Ef61DDrDj53cWQ2c8ga+YHOmXRd tPKZnYjfat+MWNabYHw3kxhuU8JI3kUEhSDEceSzZZDTjDUfewVuZsdErqaVwGErip Z4WkKtliIdbwkVjGeAJgoT4kSL8iV4+oCBTF1pEYpGChI+cootFZnBmExqOMhzbr7W LdxPCnmf2P2tg== From: SeongJae Park To: akpm@linux-foundation.org Cc: rientjes@google.com, xhao@linux.alibaba.com, linux-damon@amazon.com, linux-mm@kvack.org, linux-kernel@vger.kernel.org, SeongJae Park Subject: [RFC PATCH 4/4] selftests/damon: Add a test for DAMON sysfs interface Date: Thu, 17 Feb 2022 16:19:38 +0000 Message-Id: <20220217161938.8874-5-sj@kernel.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220217161938.8874-1-sj@kernel.org> References: <20220217161938.8874-1-sj@kernel.org> X-Rspamd-Server: rspam12 X-Rspamd-Queue-Id: 56540120002 X-Stat-Signature: rkf5xdjsnbim7ythx77tkn5sj5839xdf X-Rspam-User: Authentication-Results: imf29.hostedemail.com; dkim=pass header.d=kernel.org header.s=k20201202 header.b=RB2Y70pW; spf=pass (imf29.hostedemail.com: domain of sj@kernel.org designates 139.178.84.217 as permitted sender) smtp.mailfrom=sj@kernel.org; dmarc=pass (policy=none) header.from=kernel.org X-HE-Tag: 1645114790-82592 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000001, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: This commit adds a selftest for DAMON sysfs interface. It tests functionality of 'nr' files and existence of files in each directory of the hierarchy. Signed-off-by: SeongJae Park --- tools/testing/selftests/damon/Makefile | 1 + tools/testing/selftests/damon/sysfs.sh | 200 +++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100755 tools/testing/selftests/damon/sysfs.sh diff --git a/tools/testing/selftests/damon/Makefile b/tools/testing/selftests/damon/Makefile index 937d36ae9a69..0470c5f3e690 100644 --- a/tools/testing/selftests/damon/Makefile +++ b/tools/testing/selftests/damon/Makefile @@ -6,5 +6,6 @@ TEST_GEN_FILES += huge_count_read_write TEST_FILES = _chk_dependency.sh _debugfs_common.sh TEST_PROGS = debugfs_attrs.sh debugfs_schemes.sh debugfs_target_ids.sh TEST_PROGS += debugfs_empty_targets.sh debugfs_huge_count_read_write.sh +TEST_PROGS += sysfs.sh include ../lib.mk diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh new file mode 100755 index 000000000000..280fd6fabcd7 --- /dev/null +++ b/tools/testing/selftests/damon/sysfs.sh @@ -0,0 +1,200 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Kselftest frmework requirement - SKIP code is 4. +ksft_skip=4 + +ensure_write_succ() +{ + file=$1 + content=$2 + reason=$3 + + if ! echo "$content" > "$file" + then + echo "writing $content to $file failed" + echo "expected success because $reason" + exit 1 + fi +} + +ensure_write_fail() +{ + file=$1 + content=$2 + reason=$3 + + if echo "$content" > "$file" + then + echo "writing $content to $file succeed ($fail_reason)" + echo "expected failure because $reason" + exit 1 + fi +} + +ensure_dir() +{ + dir=$1 + to_ensure=$2 + if [ "$to_ensure" = "exist" ] && [ ! -d "$dir" ] + then + echo "$dir dir is expected but not found" + exit 1 + elif [ "$to_ensure" = "not_exist" ] && [ -d "$dir" ] + then + echo "$dir dir is not expected but found" + exit 1 + fi +} + +ensure_file() +{ + file=$1 + to_ensure=$2 + permission=$3 + if [ "$to_ensure" = "exist" ] + then + if [ ! -f "$file" ] + then + echo "$file is expected but not found" + exit 1 + fi + perm=$(stat -c "%a" "$file") + if [ ! "$perm" = "$permission" ] + then + echo "$file permission: expected $permission but $perm" + exit 1 + fi + elif [ "$to_ensure" = "not_exist" ] && [ -f "$dir" ] + then + echo "$file is not expected but found" + exit 1 + fi +} + +test_range() +{ + range_dir=$1 + ensure_dir "$range_dir" "exist" + ensure_file "$range_dir/min" "exist" 600 + ensure_file "$range_dir/max" "exist" 600 +} + +test_target() +{ + target_dir=$1 + ensure_dir "$target_dir" "exist" + ensure_file "$target_dir/pid" "exist" "600" +} + +test_targets() +{ + targets_dir=$1 + ensure_dir "$targets_dir" "exist" + ensure_file "$targets_dir/nr" "exist" 600 + + ensure_write_succ "$targets_dir/nr" "1" "valid input" + test_target "$targets_dir/0" + + ensure_write_succ "$targets_dir/nr" "2" "valid input" + test_target "$targets_dir/0" + test_target "$targets_dir/1" + + ensure_write_succ "$targets_dir/nr" "0" "valid input" + ensure_dir "$targets_dir/0" "not_exist" + ensure_dir "$targets_dir/1" "not_exist" +} + +test_intervals() +{ + intervals_dir=$1 + ensure_dir "$intervals_dir" "exist" + ensure_file "$intervals_dir/aggr_us" "exist" "600" + ensure_file "$intervals_dir/sample_us" "exist" "600" + ensure_file "$intervals_dir/update_us" "exist" "600" +} + +test_monitoring_attrs() +{ + monitoring_attrs_dir=$1 + ensure_dir "$monitoring_attrs_dir" "exist" + test_intervals "$monitoring_attrs_dir/intervals" + test_range "$monitoring_attrs_dir/nr_regions" +} + +test_context() +{ + context_dir=$1 + ensure_dir "$context_dir" "exist" + ensure_file "$context_dir/operations" "exist" 600 + test_monitoring_attrs "$context_dir/monitoring_attrs" + test_targets "$context_dir/targets" +} + +test_contexts() +{ + contexts_dir=$1 + ensure_dir "$contexts_dir" "exist" + ensure_file "$contexts_dir/nr" "exist" 600 + + ensure_write_succ "$contexts_dir/nr" "1" "valid input" + test_context "$contexts_dir/0" + + ensure_write_fail "$contexts_dir/nr" "2" "only 0/1 are supported" + test_context "$contexts_dir/0" + + ensure_write_succ "$contexts_dir/nr" "0" "valid input" + ensure_dir "$contexts_dir/0" "not_exist" +} + +test_kdamond() +{ + kdamond_dir=$1 + ensure_dir "$kdamond_dir" "exist" + ensure_file "$kdamond_dir/state" "exist" "600" + ensure_file "$kdamond_dir/pid" "exist" 400 + test_contexts "$kdamond_dir/contexts" +} + +test_kdamonds() +{ + kdamonds_dir=$1 + ensure_dir "$kdamonds_dir" "exist" + + ensure_file "$kdamonds_dir/nr" "exist" "600" + + ensure_write_succ "$kdamonds_dir/nr" "1" "valid input" + test_kdamond "$kdamonds_dir/0" + + ensure_write_succ "$kdamonds_dir/nr" "2" "valid input" + test_kdamond "$kdamonds_dir/0" + test_kdamond "$kdamonds_dir/1" + + ensure_write_succ "$kdamonds_dir/nr" "0" "valid input" + ensure_dir "$kdamonds_dir/0" "not_exist" + ensure_dir "$kdamonds_dir/1" "not_exist" +} + +test_damon_sysfs() +{ + damon_sysfs=$1 + if [ ! -d "$damon_sysfs" ] + then + echo "$damon_sysfs not found" + exit $ksft_skip + fi + + test_kdamonds "$damon_sysfs/kdamonds" +} + +check_dependencies() +{ + if [ $EUID -ne 0 ] + then + echo "Run as root" + exit $ksft_skip + fi +} + +check_dependencies +test_damon_sysfs "/sys/kernel/mm/damon/admin"