@@ -134,14 +134,6 @@ struct damos {
* in case of virtual memory monitoring) and applies the changes for each
* @regions_update_interval. All time intervals are in micro-seconds.
*
- * @rbuf: In-memory buffer for monitoring result recording.
- * @rbuf_len: The length of @rbuf.
- * @rbuf_offset: The offset for next write to @rbuf.
- * @rfile_path: Record file path.
- *
- * If @rbuf, @rbuf_len, and @rfile_path are set, the monitored results are
- * automatically stored in @rfile_path file.
- *
* @kdamond: Kernel thread who does the monitoring.
* @kdamond_stop: Notifies whether kdamond should stop.
* @kdamond_lock: Mutex for the synchronizations with @kdamond.
@@ -164,6 +156,8 @@ struct damos {
* @targets_list: Head of monitoring targets (&damon_target) list.
* @schemes_list: Head of schemes (&damos) list.
*
+ * @private Private user data.
+ *
* @init_target_regions: Constructs initial monitoring target regions.
* @update_target_regions: Updates monitoring target regions.
* @prepare_access_checks: Prepares next access check of target regions.
@@ -214,11 +208,6 @@ struct damon_ctx {
struct timespec64 last_aggregation;
struct timespec64 last_regions_update;
- unsigned char *rbuf;
- unsigned int rbuf_len;
- unsigned int rbuf_offset;
- char *rfile_path;
-
struct task_struct *kdamond;
bool kdamond_stop;
struct mutex kdamond_lock;
@@ -226,6 +215,8 @@ struct damon_ctx {
struct list_head targets_list; /* 'damon_target' objects */
struct list_head schemes_list; /* 'damos' objects */
+ void *private;
+
/* callbacks */
void (*init_target_regions)(struct damon_ctx *context);
void (*update_target_regions)(struct damon_ctx *context);
@@ -241,10 +232,6 @@ struct damon_ctx {
#ifdef CONFIG_DAMON
-#define MIN_RECORD_BUFFER_LEN 1024
-#define MAX_RECORD_BUFFER_LEN (4 * 1024 * 1024)
-#define MAX_RFILE_PATH_LEN 256
-
#define damon_next_region(r) \
(container_of(r->list.next, struct damon_region, list))
@@ -298,8 +285,6 @@ int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int,
unsigned long min_nr_reg, unsigned long max_nr_reg);
int damon_set_schemes(struct damon_ctx *ctx,
struct damos **schemes, ssize_t nr_schemes);
-int damon_set_recording(struct damon_ctx *ctx,
- unsigned int rbuf_len, char *rfile_path);
int damon_nr_running_ctxs(void);
int damon_start(struct damon_ctx *ctxs, int nr_ctxs);
@@ -36,6 +36,17 @@ static void damon_test_regions(struct kunit *test)
damon_free_target(t);
}
+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 void damon_test_target(struct kunit *test)
{
struct damon_ctx *c = damon_new_ctx();
@@ -54,23 +65,6 @@ static void damon_test_target(struct kunit *test)
damon_destroy_ctx(c);
}
-static void damon_test_set_recording(struct kunit *test)
-{
- struct damon_ctx *ctx = damon_new_ctx();
- int err;
-
- err = damon_set_recording(ctx, 42, "foo");
- KUNIT_EXPECT_EQ(test, err, -EINVAL);
- damon_set_recording(ctx, 4242, "foo.bar");
- KUNIT_EXPECT_EQ(test, ctx->rbuf_len, 4242u);
- KUNIT_EXPECT_STREQ(test, ctx->rfile_path, "foo.bar");
- damon_set_recording(ctx, 424242, "foo");
- KUNIT_EXPECT_EQ(test, ctx->rbuf_len, 424242u);
- KUNIT_EXPECT_STREQ(test, ctx->rfile_path, "foo");
-
- damon_destroy_ctx(ctx);
-}
-
/*
* Test kdamond_reset_aggregated()
*
@@ -91,9 +85,7 @@ static void damon_test_aggregate(struct kunit *test)
struct damon_target *t;
struct damon_region *r;
int it, ir;
- ssize_t sz, sr, sp;
- damon_set_recording(ctx, 4242, "damon.data");
damon_set_targets(ctx, target_ids, 3);
it = 0;
@@ -121,31 +113,6 @@ static void damon_test_aggregate(struct kunit *test)
/* targets also should be preserved */
KUNIT_EXPECT_EQ(test, 3, it);
- /* The aggregated information should be written in the buffer */
- sr = sizeof(r->ar.start) + sizeof(r->ar.end) + sizeof(r->nr_accesses);
- sp = sizeof(t->id) + sizeof(unsigned int) + 3 * sr;
- sz = sizeof(struct timespec64) + sizeof(unsigned int) + 3 * sp;
- KUNIT_EXPECT_EQ(test, (unsigned int)sz, ctx->rbuf_offset);
-
- damon_destroy_ctx(ctx);
-}
-
-static void damon_test_write_rbuf(struct kunit *test)
-{
- struct damon_ctx *ctx = damon_new_ctx();
- char *data;
-
- damon_set_recording(ctx, 4242, "damon.data");
-
- data = "hello";
- damon_write_rbuf(ctx, data, strnlen(data, 256));
- KUNIT_EXPECT_EQ(test, ctx->rbuf_offset, 5u);
-
- damon_write_rbuf(ctx, data, 0);
- KUNIT_EXPECT_EQ(test, ctx->rbuf_offset, 5u);
-
- KUNIT_EXPECT_STREQ(test, (char *)ctx->rbuf, data);
-
damon_destroy_ctx(ctx);
}
@@ -267,9 +234,7 @@ static void damon_test_split_regions_of(struct kunit *test)
static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damon_test_target),
KUNIT_CASE(damon_test_regions),
- KUNIT_CASE(damon_test_set_recording),
KUNIT_CASE(damon_test_aggregate),
- KUNIT_CASE(damon_test_write_rbuf),
KUNIT_CASE(damon_test_split_at),
KUNIT_CASE(damon_test_merge_two),
KUNIT_CASE(damon_test_merge_regions_of),
@@ -208,11 +208,6 @@ struct damon_ctx *damon_new_ctx(void)
ktime_get_coarse_ts64(&ctx->last_aggregation);
ctx->last_regions_update = ctx->last_aggregation;
- if (damon_set_recording(ctx, 0, "none")) {
- kfree(ctx);
- return NULL;
- }
-
mutex_init(&ctx->kdamond_lock);
INIT_LIST_HEAD(&ctx->targets_list);
@@ -328,54 +323,6 @@ int damon_set_schemes(struct damon_ctx *ctx, struct damos **schemes,
return 0;
}
-/**
- * damon_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.
- */
-int damon_set_recording(struct damon_ctx *ctx,
- unsigned int rbuf_len, char *rfile_path)
-{
- 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;
- }
- ctx->rbuf_len = rbuf_len;
- kfree(ctx->rbuf);
- ctx->rbuf = NULL;
- kfree(ctx->rfile_path);
- ctx->rfile_path = NULL;
-
- if (rbuf_len) {
- ctx->rbuf = kvmalloc(rbuf_len, GFP_KERNEL);
- if (!ctx->rbuf)
- return -ENOMEM;
- }
- ctx->rfile_path = kmalloc(rfile_path_len + 1, GFP_KERNEL);
- if (!ctx->rfile_path)
- return -ENOMEM;
- strncpy(ctx->rfile_path, rfile_path, rfile_path_len + 1);
- return 0;
-}
-
/**
* damon_nr_running_ctxs() - Return number of currently running contexts.
*/
@@ -390,17 +337,6 @@ int damon_nr_running_ctxs(void)
return nr_ctxs;
}
-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;
-}
-
/* Returns the size upper limit for each monitoring region */
static unsigned long damon_region_sz_limit(struct damon_ctx *ctx)
{
@@ -613,87 +549,18 @@ static bool kdamond_aggregate_interval_passed(struct damon_ctx *ctx)
}
/*
- * Flush the content in the result buffer to the result file
- */
-static void damon_flush_rbuffer(struct damon_ctx *ctx)
-{
- ssize_t sz;
- loff_t pos = 0;
- struct file *rfile;
-
- if (!ctx->rbuf_offset)
- return;
-
- rfile = filp_open(ctx->rfile_path,
- O_CREAT | O_RDWR | O_APPEND | O_LARGEFILE, 0644);
- if (IS_ERR(rfile)) {
- pr_err("Cannot open the result file %s\n",
- ctx->rfile_path);
- return;
- }
-
- while (ctx->rbuf_offset) {
- sz = kernel_write(rfile, ctx->rbuf, ctx->rbuf_offset, &pos);
- if (sz < 0)
- break;
- ctx->rbuf_offset -= sz;
- }
- filp_close(rfile, NULL);
-}
-
-/*
- * Write a data into the result buffer
- */
-static void damon_write_rbuf(struct damon_ctx *ctx, void *data, ssize_t size)
-{
- if (!ctx->rbuf_len || !ctx->rbuf || !ctx->rfile_path)
- return;
- if (ctx->rbuf_offset + size > ctx->rbuf_len)
- damon_flush_rbuffer(ctx);
- if (ctx->rbuf_offset + size > ctx->rbuf_len) {
- pr_warn("%s: flush failed, or wrong size given(%u, %zu)\n",
- __func__, ctx->rbuf_offset, size);
- return;
- }
-
- memcpy(&ctx->rbuf[ctx->rbuf_offset], data, size);
- ctx->rbuf_offset += size;
-}
-
-/*
- * Flush the aggregated monitoring results to the result buffer
- *
- * Stores current tracking results to the result buffer and reset 'nr_accesses'
- * of each region. 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>
+ * Reset the aggregated monitoring results ('nr_accesses' of each region).
*/
static void kdamond_reset_aggregated(struct damon_ctx *c)
{
struct damon_target *t;
- struct timespec64 now;
unsigned int nr;
- ktime_get_coarse_ts64(&now);
-
- damon_write_rbuf(c, &now, sizeof(now));
- nr = nr_damon_targets(c);
- damon_write_rbuf(c, &nr, sizeof(nr));
-
damon_for_each_target(t, c) {
struct damon_region *r;
- damon_write_rbuf(c, &t->id, sizeof(t->id));
nr = damon_nr_regions(t);
- damon_write_rbuf(c, &nr, sizeof(nr));
damon_for_each_region(r, t) {
- damon_write_rbuf(c, &r->ar.start, sizeof(r->ar.start));
- damon_write_rbuf(c, &r->ar.end, sizeof(r->ar.end));
- damon_write_rbuf(c, &r->nr_accesses,
- sizeof(r->nr_accesses));
trace_damon_aggregated(t, r, nr);
r->last_nr_accesses = r->nr_accesses;
r->nr_accesses = 0;
@@ -927,14 +794,6 @@ static bool kdamond_need_stop(struct damon_ctx *ctx)
return true;
}
-static void kdamond_write_record_header(struct damon_ctx *ctx)
-{
- int recfmt_ver = 2;
-
- damon_write_rbuf(ctx, "damon_recfmt_ver", 16);
- damon_write_rbuf(ctx, &recfmt_ver, sizeof(recfmt_ver));
-}
-
/*
* The monitoring daemon that runs as a kernel thread
*/
@@ -951,8 +810,6 @@ static int kdamond_fn(void *data)
ctx->init_target_regions(ctx);
sz_limit = damon_region_sz_limit(ctx);
- kdamond_write_record_header(ctx);
-
while (!kdamond_need_stop(ctx)) {
if (ctx->prepare_access_checks)
ctx->prepare_access_checks(ctx);
@@ -965,10 +822,10 @@ static int kdamond_fn(void *data)
max_nr_accesses = ctx->check_accesses(ctx);
if (kdamond_aggregate_interval_passed(ctx)) {
- if (ctx->aggregate_cb)
- ctx->aggregate_cb(ctx);
kdamond_merge_regions(ctx, max_nr_accesses / 10,
sz_limit);
+ if (ctx->aggregate_cb)
+ ctx->aggregate_cb(ctx);
kdamond_apply_schemes(ctx);
kdamond_reset_aggregated(ctx);
kdamond_split_regions(ctx);
@@ -980,7 +837,6 @@ static int kdamond_fn(void *data)
sz_limit = damon_region_sz_limit(ctx);
}
}
- damon_flush_rbuffer(ctx);
damon_for_each_target(t, ctx) {
damon_for_each_region_safe(r, next, t)
damon_destroy_region(r);
@@ -78,7 +78,7 @@ static void damon_dbgfs_test_str_to_target_ids(struct kunit *test)
static void damon_dbgfs_test_set_targets(struct kunit *test)
{
- struct damon_ctx *ctx = damon_new_ctx();
+ struct damon_ctx *ctx = debugfs_new_ctx();
unsigned long ids[] = {1, 2, 3};
char buf[64];
@@ -105,9 +105,91 @@ static void damon_dbgfs_test_set_targets(struct kunit *test)
sprint_target_ids(ctx, buf, 64);
KUNIT_EXPECT_STREQ(test, (char *)buf, "\n");
+ debugfs_destroy_ctx(ctx);
+}
+
+static void damon_dbgfs_test_set_recording(struct kunit *test)
+{
+ struct damon_ctx *ctx = debugfs_new_ctx();
+ struct debugfs_recorder *rec = ctx->private;
+ int err;
+
+ err = debugfs_set_recording(ctx, 42, "foo");
+ KUNIT_EXPECT_EQ(test, err, -EINVAL);
+ debugfs_set_recording(ctx, 4242, "foo.bar");
+ KUNIT_EXPECT_EQ(test, rec->rbuf_len, 4242u);
+ KUNIT_EXPECT_STREQ(test, rec->rfile_path, "foo.bar");
+ debugfs_set_recording(ctx, 424242, "foo");
+ KUNIT_EXPECT_EQ(test, rec->rbuf_len, 424242u);
+ KUNIT_EXPECT_STREQ(test, rec->rfile_path, "foo");
+
+ debugfs_destroy_ctx(ctx);
+}
+
+static void damon_dbgfs_test_write_rbuf(struct kunit *test)
+{
+ struct damon_ctx *ctx = debugfs_new_ctx();
+ struct debugfs_recorder *rec = ctx->private;
+ char *data;
+
+ debugfs_set_recording(ctx, 4242, "damon.data");
+
+ data = "hello";
+ debugfs_write_rbuf(ctx, data, strnlen(data, 256));
+ KUNIT_EXPECT_EQ(test, rec->rbuf_offset, 5u);
+
+ debugfs_write_rbuf(ctx, data, 0);
+ KUNIT_EXPECT_EQ(test, rec->rbuf_offset, 5u);
+
+ KUNIT_EXPECT_STREQ(test, (char *)rec->rbuf, data);
+
+ debugfs_destroy_ctx(ctx);
+}
+
+/*
+ * Test debugfs_aggregate_cb()
+ *
+ * dbgfs sets debugfs_aggregate_cb() as aggregate callback. It stores the
+ * aggregated monitoring information ('->nr_accesses' of each regions) to the
+ * result buffer.
+ */
+static void damon_dbgfs_test_aggregate(struct kunit *test)
+{
+ struct damon_ctx *ctx = debugfs_new_ctx();
+ struct debugfs_recorder *rec = ctx->private;
+ unsigned long target_ids[] = {1, 2, 3};
+ unsigned long saddr[][3] = {{10, 20, 30}, {5, 42, 49}, {13, 33, 55} };
+ unsigned long eaddr[][3] = {{15, 27, 40}, {31, 45, 55}, {23, 44, 66} };
+ unsigned long accesses[][3] = {{42, 95, 84}, {10, 20, 30}, {0, 1, 2} };
+ struct damon_target *t;
+ struct damon_region *r;
+ int it, ir;
+ ssize_t sz, sr, sp;
+
+ debugfs_set_recording(ctx, 4242, "damon.data");
+ damon_set_targets(ctx, target_ids, 3);
+
+ it = 0;
+ damon_for_each_target(t, ctx) {
+ for (ir = 0; ir < 3; ir++) {
+ r = damon_new_region(saddr[it][ir], eaddr[it][ir]);
+ r->nr_accesses = accesses[it][ir];
+ damon_add_region(r, t);
+ }
+ it++;
+ }
+ debugfs_aggregate_cb(ctx);
+
+ /* The aggregated information should be written in the buffer */
+ sr = sizeof(r->ar.start) + sizeof(r->ar.end) + sizeof(r->nr_accesses);
+ sp = sizeof(t->id) + sizeof(unsigned int) + 3 * sr;
+ sz = sizeof(struct timespec64) + sizeof(unsigned int) + 3 * sp;
+ KUNIT_EXPECT_EQ(test, (unsigned int)sz, rec->rbuf_offset);
+
damon_destroy_ctx(ctx);
}
+
static void damon_dbgfs_test_set_init_regions(struct kunit *test)
{
struct damon_ctx *ctx = damon_new_ctx();
@@ -164,6 +246,9 @@ static void damon_dbgfs_test_set_init_regions(struct kunit *test)
static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damon_dbgfs_test_str_to_target_ids),
KUNIT_CASE(damon_dbgfs_test_set_targets),
+ KUNIT_CASE(damon_dbgfs_test_set_recording),
+ KUNIT_CASE(damon_dbgfs_test_write_rbuf),
+ KUNIT_CASE(damon_dbgfs_test_aggregate),
KUNIT_CASE(damon_dbgfs_test_set_init_regions),
{},
};
@@ -10,15 +10,155 @@
#include <linux/damon.h>
#include <linux/debugfs.h>
#include <linux/file.h>
+#include <linux/mm.h>
#include <linux/module.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 debugfs_recorder {
+ unsigned char *rbuf;
+ unsigned int rbuf_len;
+ unsigned int rbuf_offset;
+ char *rfile_path;
+};
+
/* Monitoring contexts for debugfs interface users. */
static struct damon_ctx **debugfs_ctxs;
static int debugfs_nr_ctxs = 1;
static DEFINE_MUTEX(damon_dbgfs_lock);
+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;
+}
+
+/*
+ * Flush the content in the result buffer to the result file
+ */
+static void debugfs_flush_rbuffer(struct debugfs_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 debugfs_write_rbuf(struct damon_ctx *ctx, void *data, ssize_t size)
+{
+ struct debugfs_recorder *rec = (struct debugfs_recorder *)ctx->private;
+
+ if (!rec->rbuf_len || !rec->rbuf || !rec->rfile_path)
+ return;
+ if (rec->rbuf_offset + size > rec->rbuf_len)
+ debugfs_flush_rbuffer(ctx->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 debugfs_write_record_header(struct damon_ctx *ctx)
+{
+ int recfmt_ver = 2;
+
+ debugfs_write_rbuf(ctx, "damon_recfmt_ver", 16);
+ debugfs_write_rbuf(ctx, &recfmt_ver, sizeof(recfmt_ver));
+}
+
+static void debugfs_init_vm_regions(struct damon_ctx *ctx)
+{
+ debugfs_write_record_header(ctx);
+ kdamond_init_vm_regions(ctx);
+}
+
+static void debugfs_vm_cleanup(struct damon_ctx *ctx)
+{
+ debugfs_flush_rbuffer(ctx->private);
+ kdamond_vm_cleanup(ctx);
+}
+
+static void debugfs_init_phys_regions(struct damon_ctx *ctx)
+{
+ debugfs_write_record_header(ctx);
+}
+
+static void debugfs_phys_cleanup(struct damon_ctx *ctx)
+{
+ debugfs_flush_rbuffer(ctx->private);
+}
+
+/*
+ * 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 void debugfs_aggregate_cb(struct damon_ctx *c)
+{
+ struct damon_target *t;
+ struct timespec64 now;
+ unsigned int nr;
+
+ ktime_get_coarse_ts64(&now);
+
+ debugfs_write_rbuf(c, &now, sizeof(now));
+ nr = nr_damon_targets(c);
+ debugfs_write_rbuf(c, &nr, sizeof(nr));
+
+ damon_for_each_target(t, c) {
+ struct damon_region *r;
+
+ debugfs_write_rbuf(c, &t->id, sizeof(t->id));
+ nr = damon_nr_regions(t);
+ debugfs_write_rbuf(c, &nr, sizeof(nr));
+ damon_for_each_region(r, t) {
+ debugfs_write_rbuf(c, &r->ar.start, sizeof(r->ar.start));
+ debugfs_write_rbuf(c, &r->ar.end, sizeof(r->ar.end));
+ debugfs_write_rbuf(c, &r->nr_accesses,
+ sizeof(r->nr_accesses));
+ }
+ }
+}
+
static ssize_t debugfs_monitor_on_read(struct file *file,
char __user *buf, size_t count, loff_t *ppos)
{
@@ -330,6 +470,20 @@ static struct pid *damon_get_pidfd_pid(unsigned int pidfd)
return pid;
}
+static void debugfs_set_vaddr_primitives(struct damon_ctx *ctx)
+{
+ damon_set_vaddr_primitives(ctx);
+ ctx->init_target_regions = debugfs_init_vm_regions;
+ ctx->cleanup = debugfs_vm_cleanup;
+}
+
+static void debugfs_set_paddr_primitives(struct damon_ctx *ctx)
+{
+ damon_set_paddr_primitives(ctx);
+ ctx->init_target_regions = debugfs_init_phys_regions;
+ ctx->cleanup = debugfs_phys_cleanup;
+}
+
static ssize_t debugfs_target_ids_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
@@ -349,12 +503,13 @@ static ssize_t debugfs_target_ids_write(struct file *file,
nrs = kbuf;
if (!strncmp(kbuf, "paddr\n", count)) {
/* Configure the context for physical memory monitoring */
- damon_set_paddr_primitives(ctx);
+ debugfs_set_paddr_primitives(ctx);
/* target id is meaningless here, but we set it just for fun */
scnprintf(kbuf, count, "42 ");
} else {
/* Configure the context for virtual memory monitoring */
- damon_set_vaddr_primitives(ctx);
+ debugfs_set_vaddr_primitives(ctx);
+
if (!strncmp(kbuf, "pidfd ", 6)) {
received_pidfds = true;
nrs = &kbuf[6];
@@ -398,16 +553,76 @@ static ssize_t debugfs_record_read(struct file *file,
char __user *buf, size_t count, loff_t *ppos)
{
struct damon_ctx *ctx = file->private_data;
+ struct debugfs_recorder *rec = ctx->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",
- ctx->rbuf_len, ctx->rfile_path);
+ rec->rbuf_len, rec->rfile_path);
mutex_unlock(&ctx->kdamond_lock);
return simple_read_from_buffer(buf, count, ppos, record_buf, ret);
}
+/*
+ * debugfs_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 debugfs_set_recording(struct damon_ctx *ctx,
+ unsigned int rbuf_len, char *rfile_path)
+{
+ struct debugfs_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->private;
+ if (!recorder) {
+ recorder = kzalloc(sizeof(*recorder), GFP_KERNEL);
+ if (!recorder)
+ return -ENOMEM;
+ ctx->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 debugfs_record_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
@@ -434,7 +649,7 @@ static ssize_t debugfs_record_write(struct file *file,
goto unlock_out;
}
- err = damon_set_recording(ctx, rbuf_len, rfile_path);
+ err = debugfs_set_recording(ctx, rbuf_len, rfile_path);
if (err)
ret = err;
unlock_out:
@@ -654,6 +869,38 @@ static struct dentry **debugfs_dirs;
static int debugfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx);
+static void debugfs_free_recorder(struct debugfs_recorder *recorder)
+{
+ kfree(recorder->rbuf);
+ kfree(recorder->rfile_path);
+ kfree(recorder);
+}
+
+static struct damon_ctx *debugfs_new_ctx(void)
+{
+ struct damon_ctx *ctx;
+
+ ctx = damon_new_ctx();
+ if (!ctx)
+ return NULL;
+
+ if (debugfs_set_recording(ctx, 0, "none")) {
+ damon_destroy_ctx(ctx);
+ return NULL;
+ }
+
+ debugfs_set_vaddr_primitives(ctx);
+ ctx->aggregate_cb = debugfs_aggregate_cb;
+ return ctx;
+}
+
+static void debugfs_destroy_ctx(struct damon_ctx *ctx)
+{
+ debugfs_free_recorder(ctx->private);
+ damon_destroy_ctx(ctx);
+}
+
+
static ssize_t debugfs_nr_contexts_write(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
@@ -689,7 +936,7 @@ static ssize_t debugfs_nr_contexts_write(struct file *file,
for (i = nr_contexts; i < debugfs_nr_ctxs; i++) {
debugfs_remove(debugfs_dirs[i]);
- damon_destroy_ctx(debugfs_ctxs[i]);
+ debugfs_destroy_ctx(debugfs_ctxs[i]);
}
new_dirs = kmalloc_array(nr_contexts, sizeof(*new_dirs), GFP_KERNEL);
@@ -729,13 +976,13 @@ static ssize_t debugfs_nr_contexts_write(struct file *file,
break;
}
- debugfs_ctxs[i] = damon_new_ctx();
+ debugfs_ctxs[i] = debugfs_new_ctx();
if (!debugfs_ctxs[i]) {
pr_err("ctx for %s creation failed\n", dirname);
+ debugfs_remove(debugfs_dirs[i]);
ret = -ENOMEM;
break;
}
- damon_set_vaddr_primitives(debugfs_ctxs[i]);
if (debugfs_fill_ctx_dir(debugfs_dirs[i], debugfs_ctxs[i])) {
ret = -ENOMEM;
@@ -865,10 +1112,9 @@ static int __init damon_dbgfs_init(void)
int rc;
debugfs_ctxs = kmalloc(sizeof(*debugfs_ctxs), GFP_KERNEL);
- debugfs_ctxs[0] = damon_new_ctx();
+ debugfs_ctxs[0] = debugfs_new_ctx();
if (!debugfs_ctxs[0])
return -ENOMEM;
- damon_set_vaddr_primitives(debugfs_ctxs[0]);
rc = damon_debugfs_init();
if (rc)