From: SeongJae Park <sjpark@amazon.com>
To: <akpm@linux-foundation.org>
Cc: SeongJae Park <sjpark@amazon.de>, <Jonathan.Cameron@Huawei.com>,
<aarcange@redhat.com>, <acme@kernel.org>,
<alexander.shishkin@linux.intel.com>, <amit@kernel.org>,
<benh@kernel.crashing.org>, <brendan.d.gregg@gmail.com>,
<brendanhiggins@google.com>, <cai@lca.pw>,
<colin.king@canonical.com>, <corbet@lwn.net>, <david@redhat.com>,
<dwmw@amazon.com>, <elver@google.com>, <fan.du@intel.com>,
<foersleo@amazon.de>, <gthelen@google.com>, <irogers@google.com>,
<jolsa@redhat.com>, <kirill@shutemov.name>,
<mark.rutland@arm.com>, <mgorman@suse.de>, <minchan@kernel.org>,
<mingo@redhat.com>, <namhyung@kernel.org>, <peterz@infradead.org>,
<rdunlap@infradead.org>, <riel@surriel.com>,
<rientjes@google.com>, <rostedt@goodmis.org>, <rppt@kernel.org>,
<sblbir@amazon.com>, <shakeelb@google.com>, <shuah@kernel.org>,
<sj38.park@gmail.com>, <snu@amazon.de>, <vbabka@suse.cz>,
<vdavydov.dev@gmail.com>, <yang.shi@linux.alibaba.com>,
<ying.huang@intel.com>, <zgf574564920@gmail.com>,
<linux-damon@amazon.com>, <linux-mm@kvack.org>,
<linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Subject: [PATCH v23 08/15] mm/damon/dbgfs: Implement recording feature
Date: Tue, 15 Dec 2020 12:54:41 +0100 [thread overview]
Message-ID: <20201215115448.25633-9-sjpark@amazon.com> (raw)
In-Reply-To: <20201215115448.25633-1-sjpark@amazon.com>
From: SeongJae Park <sjpark@amazon.de>
The user space users can control DAMON and get the monitoring results
via the 'damon_aggregated' tracepoint event. However, dealing with the
tracepoint might be complex for some simple use cases. This commit
therefore implements 'recording' feature in 'damon-dbgfs'. The feature
can be used via 'record' file in the '<debugfs>/damon/' directory.
The file allows users to record monitored access patterns in a regular
binary file. The recorded results are first written in an in-memory
buffer and flushed to a file in batch. Users can get and set the size
of the buffer and the path to the result file by reading from and
writing to the ``record`` file. For example, below commands set the
buffer to be 4 KiB and the result to be saved in ``/damon.data``. ::
# cd <debugfs>/damon
# echo "4096 /damon.data" > record
# cat record
4096 /damon.data
The recording can be disabled by setting the buffer size zero.
Signed-off-by: SeongJae Park <sjpark@amazon.de>
---
mm/damon/dbgfs.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 259 insertions(+), 2 deletions(-)
diff --git a/mm/damon/dbgfs.c b/mm/damon/dbgfs.c
index fd1665a183c2..733d2f3e646e 100644
--- a/mm/damon/dbgfs.c
+++ b/mm/damon/dbgfs.c
@@ -15,6 +15,17 @@
#include <linux/page_idle.h>
#include <linux/slab.h>
+#define MIN_RECORD_BUFFER_LEN 1024
+#define MAX_RECORD_BUFFER_LEN (4 * 1024 * 1024)
+#define MAX_RFILE_PATH_LEN 256
+
+struct dbgfs_recorder {
+ unsigned char *rbuf;
+ unsigned int rbuf_len;
+ unsigned int rbuf_offset;
+ char *rfile_path;
+};
+
static struct damon_ctx **dbgfs_ctxs;
static int dbgfs_nr_ctxs;
static struct dentry **dbgfs_dirs;
@@ -97,6 +108,116 @@ static ssize_t dbgfs_attrs_write(struct file *file,
return ret;
}
+static ssize_t dbgfs_record_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct damon_ctx *ctx = file->private_data;
+ struct dbgfs_recorder *rec = ctx->callback.private;
+ char record_buf[20 + MAX_RFILE_PATH_LEN];
+ int ret;
+
+ mutex_lock(&ctx->kdamond_lock);
+ ret = scnprintf(record_buf, ARRAY_SIZE(record_buf), "%u %s\n",
+ rec->rbuf_len, rec->rfile_path);
+ mutex_unlock(&ctx->kdamond_lock);
+ return simple_read_from_buffer(buf, count, ppos, record_buf, ret);
+}
+
+/*
+ * dbgfs_set_recording() - Set attributes for the recording.
+ * @ctx: target kdamond context
+ * @rbuf_len: length of the result buffer
+ * @rfile_path: path to the monitor result files
+ *
+ * Setting 'rbuf_len' 0 disables recording.
+ *
+ * This function should not be called while the kdamond is running.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+static int dbgfs_set_recording(struct damon_ctx *ctx,
+ unsigned int rbuf_len, char *rfile_path)
+{
+ struct dbgfs_recorder *recorder;
+ size_t rfile_path_len;
+
+ if (rbuf_len && (rbuf_len > MAX_RECORD_BUFFER_LEN ||
+ rbuf_len < MIN_RECORD_BUFFER_LEN)) {
+ pr_err("result buffer size (%u) is out of [%d,%d]\n",
+ rbuf_len, MIN_RECORD_BUFFER_LEN,
+ MAX_RECORD_BUFFER_LEN);
+ return -EINVAL;
+ }
+ rfile_path_len = strnlen(rfile_path, MAX_RFILE_PATH_LEN);
+ if (rfile_path_len >= MAX_RFILE_PATH_LEN) {
+ pr_err("too long (>%d) result file path %s\n",
+ MAX_RFILE_PATH_LEN, rfile_path);
+ return -EINVAL;
+ }
+
+ recorder = ctx->callback.private;
+ if (!recorder) {
+ recorder = kzalloc(sizeof(*recorder), GFP_KERNEL);
+ if (!recorder)
+ return -ENOMEM;
+ ctx->callback.private = recorder;
+ }
+
+ recorder->rbuf_len = rbuf_len;
+ kfree(recorder->rbuf);
+ recorder->rbuf = NULL;
+ kfree(recorder->rfile_path);
+ recorder->rfile_path = NULL;
+
+ if (rbuf_len) {
+ recorder->rbuf = kvmalloc(rbuf_len, GFP_KERNEL);
+ if (!recorder->rbuf)
+ return -ENOMEM;
+ }
+ recorder->rfile_path = kmalloc(rfile_path_len + 1, GFP_KERNEL);
+ if (!recorder->rfile_path)
+ return -ENOMEM;
+ strncpy(recorder->rfile_path, rfile_path, rfile_path_len + 1);
+
+ return 0;
+}
+
+static ssize_t dbgfs_record_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct damon_ctx *ctx = file->private_data;
+ char *kbuf;
+ unsigned int rbuf_len;
+ char rfile_path[MAX_RFILE_PATH_LEN];
+ ssize_t ret = count;
+ int err;
+
+ kbuf = user_input_str(buf, count, ppos);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+ if (sscanf(kbuf, "%u %s",
+ &rbuf_len, rfile_path) != 2) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&ctx->kdamond_lock);
+ if (ctx->kdamond) {
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ err = dbgfs_set_recording(ctx, rbuf_len, rfile_path);
+ if (err)
+ ret = err;
+unlock_out:
+ mutex_unlock(&ctx->kdamond_lock);
+out:
+ kfree(kbuf);
+ return ret;
+}
+
#define targetid_is_pid(ctx) \
(ctx->primitive.target_valid == damon_va_target_valid)
@@ -230,6 +351,13 @@ static const struct file_operations attrs_fops = {
.write = dbgfs_attrs_write,
};
+static const struct file_operations record_fops = {
+ .owner = THIS_MODULE,
+ .open = damon_dbgfs_open,
+ .read = dbgfs_record_read,
+ .write = dbgfs_record_write,
+};
+
static const struct file_operations target_ids_fops = {
.owner = THIS_MODULE,
.open = damon_dbgfs_open,
@@ -239,8 +367,9 @@ static const struct file_operations target_ids_fops = {
static int dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx)
{
- const char * const file_names[] = {"attrs", "target_ids"};
- const struct file_operations *fops[] = {&attrs_fops, &target_ids_fops};
+ const char * const file_names[] = {"attrs", "record", "target_ids"};
+ const struct file_operations *fops[] = {&attrs_fops, &record_fops,
+ &target_ids_fops};
int i;
for (i = 0; i < ARRAY_SIZE(file_names); i++) {
@@ -254,6 +383,126 @@ static int dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx)
return 0;
}
+/*
+ * Flush the content in the result buffer to the result file
+ */
+static void dbgfs_flush_rbuffer(struct dbgfs_recorder *rec)
+{
+ ssize_t sz;
+ loff_t pos = 0;
+ struct file *rfile;
+
+ if (!rec->rbuf_offset)
+ return;
+
+ rfile = filp_open(rec->rfile_path,
+ O_CREAT | O_RDWR | O_APPEND | O_LARGEFILE, 0644);
+ if (IS_ERR(rfile)) {
+ pr_err("Cannot open the result file %s\n",
+ rec->rfile_path);
+ return;
+ }
+
+ while (rec->rbuf_offset) {
+ sz = kernel_write(rfile, rec->rbuf, rec->rbuf_offset, &pos);
+ if (sz < 0)
+ break;
+ rec->rbuf_offset -= sz;
+ }
+ filp_close(rfile, NULL);
+}
+
+/*
+ * Write a data into the result buffer
+ */
+static void dbgfs_write_rbuf(struct damon_ctx *ctx, void *data, ssize_t size)
+{
+ struct dbgfs_recorder *rec = ctx->callback.private;
+
+ if (!rec->rbuf_len || !rec->rbuf || !rec->rfile_path)
+ return;
+ if (rec->rbuf_offset + size > rec->rbuf_len)
+ dbgfs_flush_rbuffer(ctx->callback.private);
+ if (rec->rbuf_offset + size > rec->rbuf_len) {
+ pr_warn("%s: flush failed, or wrong size given(%u, %zu)\n",
+ __func__, rec->rbuf_offset, size);
+ return;
+ }
+
+ memcpy(&rec->rbuf[rec->rbuf_offset], data, size);
+ rec->rbuf_offset += size;
+}
+
+static void dbgfs_write_record_header(struct damon_ctx *ctx)
+{
+ int recfmt_ver = 2;
+
+ dbgfs_write_rbuf(ctx, "damon_recfmt_ver", 16);
+ dbgfs_write_rbuf(ctx, &recfmt_ver, sizeof(recfmt_ver));
+}
+
+static unsigned int nr_damon_targets(struct damon_ctx *ctx)
+{
+ struct damon_target *t;
+ unsigned int nr_targets = 0;
+
+ damon_for_each_target(t, ctx)
+ nr_targets++;
+
+ return nr_targets;
+}
+
+static int dbgfs_before_start(struct damon_ctx *ctx)
+{
+ dbgfs_write_record_header(ctx);
+ return 0;
+}
+
+/*
+ * Store the aggregated monitoring results to the result buffer
+ *
+ * The format for the result buffer is as below:
+ *
+ * <time> <number of targets> <array of target infos>
+ *
+ * target info: <id> <number of regions> <array of region infos>
+ * region info: <start address> <end address> <nr_accesses>
+ */
+static int dbgfs_after_aggregation(struct damon_ctx *c)
+{
+ struct damon_target *t;
+ struct timespec64 now;
+ unsigned int nr;
+
+ ktime_get_coarse_ts64(&now);
+
+ dbgfs_write_rbuf(c, &now, sizeof(now));
+ nr = nr_damon_targets(c);
+ dbgfs_write_rbuf(c, &nr, sizeof(nr));
+
+ damon_for_each_target(t, c) {
+ struct damon_region *r;
+
+ dbgfs_write_rbuf(c, &t->id, sizeof(t->id));
+ nr = damon_nr_regions(t);
+ dbgfs_write_rbuf(c, &nr, sizeof(nr));
+ damon_for_each_region(r, t) {
+ dbgfs_write_rbuf(c, &r->ar.start, sizeof(r->ar.start));
+ dbgfs_write_rbuf(c, &r->ar.end, sizeof(r->ar.end));
+ dbgfs_write_rbuf(c, &r->nr_accesses,
+ sizeof(r->nr_accesses));
+ }
+ }
+
+ return 0;
+}
+
+static int dbgfs_before_terminate(struct damon_ctx *ctx)
+{
+ dbgfs_flush_rbuffer(ctx->callback.private);
+ return 0;
+}
+
static struct damon_ctx *dbgfs_new_ctx(void)
{
struct damon_ctx *ctx;
@@ -262,7 +511,15 @@ static struct damon_ctx *dbgfs_new_ctx(void)
if (!ctx)
return NULL;
+ if (dbgfs_set_recording(ctx, 0, "none")) {
+ damon_destroy_ctx(ctx);
+ return NULL;
+ }
+
damon_va_set_primitives(ctx);
+ ctx->callback.before_start = dbgfs_before_start;
+ ctx->callback.after_aggregation = dbgfs_after_aggregation;
+ ctx->callback.before_terminate = dbgfs_before_terminate;
return ctx;
}
--
2.17.1
next prev parent reply other threads:[~2020-12-15 11:59 UTC|newest]
Thread overview: 38+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-12-15 11:54 [PATCH v23 00/15] Introduce Data Access MONitor (DAMON) SeongJae Park
2020-12-15 11:54 ` [PATCH v23 01/15] mm: " SeongJae Park
2020-12-23 15:11 ` Shakeel Butt
2020-12-23 16:33 ` SeongJae Park
2020-12-23 22:49 ` Shakeel Butt
2020-12-24 7:02 ` SeongJae Park
2020-12-15 11:54 ` [PATCH v23 02/15] mm/damon/core: Implement region-based sampling SeongJae Park
2021-02-01 17:37 ` Shakeel Butt
2021-02-02 9:17 ` SeongJae Park
2020-12-15 11:54 ` [PATCH v23 03/15] mm/damon: Adaptively adjust regions SeongJae Park
2021-01-19 18:36 ` SeongJae Park
2021-02-01 17:37 ` Shakeel Butt
2021-02-02 9:39 ` SeongJae Park
2020-12-15 11:54 ` [PATCH v23 04/15] mm/idle_page_tracking: Make PG_idle reusable SeongJae Park
2020-12-23 15:11 ` Shakeel Butt
2020-12-15 11:54 ` [PATCH v23 05/15] mm/damon: Implement primitives for the virtual memory address spaces SeongJae Park
2020-12-23 15:31 ` Shakeel Butt
2020-12-23 16:47 ` SeongJae Park
2020-12-23 22:54 ` Shakeel Butt
2020-12-24 7:11 ` SeongJae Park
2021-01-27 16:56 ` SeongJae Park
2021-01-27 17:02 ` Shakeel Butt
2020-12-15 11:54 ` [PATCH v23 06/15] mm/damon: Add a tracepoint SeongJae Park
2020-12-15 11:54 ` [PATCH v23 07/15] mm/damon: Implement a debugfs-based user space interface SeongJae Park
2021-02-01 17:37 ` Shakeel Butt
2021-02-02 10:00 ` SeongJae Park
2020-12-15 11:54 ` SeongJae Park [this message]
2020-12-15 11:54 ` [PATCH v23 09/15] mm/damon/dbgfs: Export kdamond pid to the user space SeongJae Park
2020-12-15 11:54 ` [PATCH v23 10/15] mm/damon/dbgfs: Support multiple contexts SeongJae Park
2021-02-02 12:27 ` SeongJae Park
2020-12-15 11:54 ` [PATCH v23 11/15] tools: Introduce a minimal user-space tool for DAMON SeongJae Park
2020-12-23 18:37 ` SeongJae Park
2020-12-23 22:56 ` Shakeel Butt
2020-12-24 7:13 ` SeongJae Park
2020-12-15 11:54 ` [PATCH v23 12/15] Documentation: Add documents " SeongJae Park
2020-12-15 11:54 ` [PATCH v23 13/15] mm/damon: Add kunit tests SeongJae Park
2020-12-15 11:54 ` [PATCH v23 14/15] mm/damon: Add user space selftests SeongJae Park
2020-12-15 11:54 ` [PATCH v23 15/15] MAINTAINERS: Update for DAMON SeongJae Park
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20201215115448.25633-9-sjpark@amazon.com \
--to=sjpark@amazon.com \
--cc=Jonathan.Cameron@Huawei.com \
--cc=aarcange@redhat.com \
--cc=acme@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=alexander.shishkin@linux.intel.com \
--cc=amit@kernel.org \
--cc=benh@kernel.crashing.org \
--cc=brendan.d.gregg@gmail.com \
--cc=brendanhiggins@google.com \
--cc=cai@lca.pw \
--cc=colin.king@canonical.com \
--cc=corbet@lwn.net \
--cc=david@redhat.com \
--cc=dwmw@amazon.com \
--cc=elver@google.com \
--cc=fan.du@intel.com \
--cc=foersleo@amazon.de \
--cc=gthelen@google.com \
--cc=irogers@google.com \
--cc=jolsa@redhat.com \
--cc=kirill@shutemov.name \
--cc=linux-damon@amazon.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mark.rutland@arm.com \
--cc=mgorman@suse.de \
--cc=minchan@kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=rdunlap@infradead.org \
--cc=riel@surriel.com \
--cc=rientjes@google.com \
--cc=rostedt@goodmis.org \
--cc=rppt@kernel.org \
--cc=sblbir@amazon.com \
--cc=shakeelb@google.com \
--cc=shuah@kernel.org \
--cc=sj38.park@gmail.com \
--cc=sjpark@amazon.de \
--cc=snu@amazon.de \
--cc=vbabka@suse.cz \
--cc=vdavydov.dev@gmail.com \
--cc=yang.shi@linux.alibaba.com \
--cc=ying.huang@intel.com \
--cc=zgf574564920@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).