Introduce Data Access MONitor (DAMON)
Subsystems (8)
v19 cover Introduce Data Access MONitor (DAMON)
From: SeongJae Park <[email protected]>
Changes from Previous Version
=============================
- Drop loadable module support (Mike Rapoport)
- Select PAGE_EXTENSION if !64BIT for 'set_page_young()'
- Take care of the MMU notification subscribers (Shakeel Butt)
- Substitute 'struct damon_task' with 'struct damon_target' for better abstract
- Use 'struct pid' instead of 'pid_t' as the target (Shakeel Butt)
- Support pidfd from the debugfs interface (Shakeel Butt)
- Fix typos (Greg Thelen)
- Properly isolate DAMON from other pmd/pte Accessed bit users (Greg Thelen)
- Rebase on v5.8
Introduction
============
DAMON is a data access monitoring framework subsystem for the Linux kernel. The core mechanisms of DAMON called 'region based sampling' and 'adaptive regions adjustment' (refer to 'mechanisms.rst' in the 11th patch of this patchset for the detail) make it
- accurate (The monitored information is useful for DRAM level memory management. It might not appropriate for Cache-level accuracy, though.),
- light-weight (The monitoring overhead is low enough to be applied online while making no impact on the performance of the target workloads.), and
- scalable (the upper-bound of the instrumentation overhead is controllable regardless of the size of target workloads.).
Using this framework, therefore, the kernel's core memory management mechanisms such as reclamation and THP can be optimized for better memory management. The experimental memory management optimization works that incurring high instrumentation overhead will be able to have another try. In user space, meanwhile, users who have some special workloads will be able to write personalized tools or applications for deeper understanding and specialized optimizations of their systems.
Evaluations
===========
We evaluated DAMON's overhead, monitoring quality and usefulness using 25 realistic workloads on my QEMU/KVM based virtual machine running a kernel that v16 DAMON patchset is applied.
DAMON is lightweight. It increases system memory usage by only -0.25% and consumes less than 1% CPU time in most case. It slows target workloads down by only 0.94%.
DAMON is accurate and useful for memory management optimizations. An experimental DAMON-based operation scheme for THP, 'ethp', removes 31.29% of THP memory overheads while preserving 60.64% of THP speedup. Another experimental DAMON-based 'proactive reclamation' implementation, 'prcl', reduces 87.95% of residential sets and 29.52% of system memory footprint while incurring only 2.15% runtime overhead in the best case (parsec3/freqmine).
NOTE that the experimentail THP optimization and proactive reclamation are not for production, just only for proof of concepts.
Please refer to the official document[1] or "Documentation/admin-guide/mm: Add a document for DAMON" patch in this patchset for detailed evaluation setup and results.
[1] https://damonitor.github.io/doc/html/latest-damon/admin-guide/mm/damon/eval.html
More Information
================
We prepared a showcase web site[1] that you can get more information. There are
- the official documentations[2],
- the heatmap format dynamic access pattern of various realistic workloads for heap area[3], mmap()-ed area[4], and stack[5] area,
- the dynamic working set size distribution[6] and chronological working set size changes[7], and
- the latest performance test results[8].
[1] https://damonitor.github.io/_index
[2] https://damonitor.github.io/doc/html/latest-damon
[3] https://damonitor.github.io/test/result/visual/latest/rec.heatmap.0.png.html
[4] https://damonitor.github.io/test/result/visual/latest/rec.heatmap.1.png.html
[5] https://damonitor.github.io/test/result/visual/latest/rec.heatmap.2.png.html
[6] https://damonitor.github.io/test/result/visual/latest/rec.wss_sz.png.html
[7] https://damonitor.github.io/test/result/visual/latest/rec.wss_time.png.html
[8] https://damonitor.github.io/test/result/perf/latest/html/index.html
Baseline and Complete Git Trees
===============================
The patches are based on the v5.8. You can also clone the complete git tree:
$ git clone git://github.com/sjp38/linux -b damon/patches/v19
The web is also available:
https://github.com/sjp38/linux/releases/tag/damon/patches/v19
There are a couple of trees for entire DAMON patchset series. It includes future features. The first one[1] contains the changes for latest release, while the other one[2] contains the changes for next release.
[1] https://github.com/sjp38/linux/tree/damon/master
[2] https://github.com/sjp38/linux/tree/damon/next
Sequence Of Patches
===================
First four patches implement the target address space independent core logics of DAMON and it's programming interface. The 1st patch introduces DAMON subsystem, it's data structures, and the data structure related basic manipulation functions. Following three patches (2nd to 4th) implements the core mechanisms of DAMON, namely regions based sampling (patch 2), adaptive regions adjustment (patch 3), and dynamic memory mapping change adoption (patch 4).
Now the essential parts of DAMON is complete but require low level primitives to be implemented and configured with DAMON to just work. The following two patches makes it just work for virtual address spaces monitoring. The 5th patch makes 'PG_idle' could be used by DAMON and the 6th patch implements the virtual memory address space specific low primitives using page table Accessed bits and the 'PG_idle' page flag.
Now DAMON just works for virtual address space monitoring via the kernel space api. Following six patches adds interfaces for the users in the user space. The 7th patch implements recording of access patterns in DAMON. Each of next two patches (8th and 9th) respectively adds a tracepoint for other tracepoints supporting tracers such as perf, and a debugfs interface for privileged people and/or programs in user space. 10th patch makes the debugfs interface further support pidfd. And, the 11th patch implements an user space tool to provide a minimal reference to the debugfs interface and for high level use/tests of the DAMON.
Three patches for maintainability follows. The 12th patch adds documentations for both the user space and the kernel space. The 13th patch provides unit tests (based on the kunit) while the 14th patch adds user space tests (based on the kselftest).
Finally, the last patch (15th) updates the MAINTAINERS file.
Patch History
=============
Changes from v18
(https://lore.kernel.org/linux-mm/[email protected]/)
- Drop loadable module support (Mike Rapoport)
- Select PAGE_EXTENSION if !64BIT for 'set_page_young()'
- Take care of the MMU notification subscribers (Shakeel Butt)
- Substitute 'struct damon_task' with 'struct damon_target' for better abstract
- Use 'struct pid' instead of 'pid_t' as the target (Shakeel Butt)
- Support pidfd from the debugfs interface (Shakeel Butt)
- Fix typos (Greg Thelen)
- Properly isolate DAMON from other pmd/pte Accessed bit users (Greg Thelen)
- Rebase on v5.8
Changes from v17
(https://lore.kernel.org/linux-mm/[email protected]/)
- Reorganize the doc and remove png blobs (Mike Rapoport)
- Wordsmith mechnisms doc and commit messages
- tools/wss: Set default working set access frequency threshold
- Avoid race in damon deamon start
Changes from v16
(https://lore.kernel.org/linux-mm/[email protected]/)
- Wordsmith/cleanup the documentations and the code
- user space tool: Simplify the code and add wss option for reuse histogram
- recording: Check disablement condition properly
- recording: Force minimal recording buffer size (1KB)
Changes from v15
(https://lore.kernel.org/linux-mm/[email protected]/)
- Refine commit messages (David Hildenbrand)
- Optimizes three vma regions search (Varad Gautam)
- Support static granularity monitoring (Shakeel Butt)
- Cleanup code and re-organize the sequence of patches
Changes from v14
(https://lore.kernel.org/linux-mm/[email protected]/)
- Directly pass region and task to tracepoint (Steven Rostedt)
- Refine comments for better read
- Add more 'Reviewed-by's (Leonard Foerster, Brendan Higgins)
Changes from v13
(https://lore.kernel.org/linux-mm/[email protected]/)
- Fix a typo (Leonard Foerster)
- Fix wring condition of three sub ranges split (Leonard Foerster)
- Rebase on v5.7
Please refer to the v13 patchset to get older history.
SeongJae Park (15):
mm: Introduce Data Access MONitor (DAMON)
mm/damon: Implement region based sampling
mm/damon: Adaptively adjust regions
mm/damon: Track dynamic monitoring target regions update mm/idle_page_tracking: Make PG_(idle|young) reusable mm/damon: Implement callbacks for the virtual memory address spaces mm/damon: Implement access pattern recording
mm/damon: Add a tracepoint
mm/damon: Implement a debugfs interface
damon/debugfs: Support pidfd target id
tools: Introduce a minimal user-space tool for DAMON
Documentation: Add documents for DAMON
mm/damon: Add kunit tests
mm/damon: Add user space selftests
MAINTAINERS: Update for DAMON
Documentation/admin-guide/mm/damon/guide.rst | 157 ++ Documentation/admin-guide/mm/damon/index.rst | 15 + Documentation/admin-guide/mm/damon/plans.rst | 29 + Documentation/admin-guide/mm/damon/start.rst | 96 + Documentation/admin-guide/mm/damon/usage.rst | 302 +++ Documentation/admin-guide/mm/index.rst | 1 + Documentation/vm/damon/api.rst | 20 + Documentation/vm/damon/design.rst | 166 ++ Documentation/vm/damon/eval.rst | 222 +++ Documentation/vm/damon/faq.rst | 58 + Documentation/vm/damon/index.rst | 31 + Documentation/vm/index.rst | 1 + MAINTAINERS | 13 + include/linux/damon.h | 188 ++ include/linux/page-flags.h | 4 +- include/linux/page_ext.h | 2 +- include/linux/page_idle.h | 6 +- include/trace/events/damon.h | 43 + include/trace/events/mmflags.h | 2 +- mm/Kconfig | 33 + mm/Makefile | 1 + mm/damon-test.h | 671 +++++++ mm/damon.c | 1732 +++++++++++++++++ mm/page_ext.c | 12 +- mm/page_idle.c | 10 - tools/damon/.gitignore | 1 + tools/damon/_damon.py | 130 ++ tools/damon/_dist.py | 36 + tools/damon/_recfile.py | 23 + tools/damon/bin2txt.py | 67 + tools/damon/damo | 37 + tools/damon/heats.py | 362 ++++ tools/damon/nr_regions.py | 91 + tools/damon/record.py | 135 ++ tools/damon/report.py | 45 + tools/damon/wss.py | 100 + tools/testing/selftests/damon/Makefile | 7 + .../selftests/damon/_chk_dependency.sh | 28 + tools/testing/selftests/damon/_chk_record.py | 109 ++ .../testing/selftests/damon/debugfs_attrs.sh | 161 ++ .../testing/selftests/damon/debugfs_record.sh | 50 + 41 files changed, 5179 insertions(+), 18 deletions(-) create mode 100644 Documentation/admin-guide/mm/damon/guide.rst create mode 100644 Documentation/admin-guide/mm/damon/index.rst create mode 100644 Documentation/admin-guide/mm/damon/plans.rst create mode 100644 Documentation/admin-guide/mm/damon/start.rst create mode 100644 Documentation/admin-guide/mm/damon/usage.rst create mode 100644 Documentation/vm/damon/api.rst create mode 100644 Documentation/vm/damon/design.rst create mode 100644 Documentation/vm/damon/eval.rst create mode 100644 Documentation/vm/damon/faq.rst create mode 100644 Documentation/vm/damon/index.rst create mode 100644 include/linux/damon.h create mode 100644 include/trace/events/damon.h create mode 100644 mm/damon-test.h create mode 100644 mm/damon.c create mode 100644 tools/damon/.gitignore create mode 100644 tools/damon/_damon.py create mode 100644 tools/damon/_dist.py create mode 100644 tools/damon/_recfile.py create mode 100644 tools/damon/bin2txt.py create mode 100755 tools/damon/damo create mode 100644 tools/damon/heats.py create mode 100644 tools/damon/nr_regions.py create mode 100644 tools/damon/record.py create mode 100644 tools/damon/report.py create mode 100644 tools/damon/wss.py create mode 100644 tools/testing/selftests/damon/Makefile create mode 100644 tools/testing/selftests/damon/_chk_dependency.sh create mode 100644 tools/testing/selftests/damon/_chk_record.py create mode 100755 tools/testing/selftests/damon/debugfs_attrs.sh create mode 100755 tools/testing/selftests/damon/debugfs_record.sh
Signature · 1 line
2.17.1
PATCH v19 1/15 mm: Introduce Data Access MONitor (DAMON)
From: SeongJae Park <[email protected]>
DAMON is a data access monitoring framework subsystem for the Linux kernel. The core mechanisms of DAMON make it
- accurate (the monitoring output is useful enough for DRAM level memory management; It might not appropriate for CPU Cache levels, though),
- light-weight (the monitoring overhead is low enough to be applied online), and
- scalable (the upper-bound of the overhead is in constant range regardless of the size of target workloads).
Using this framework, therefore, the kernel's memory management mechanisms can make advanced decisions. Experimental memory management optimization works that incurring high data accesses monitoring overhead could implemented again. In user space, meanwhile, users who have some special workloads can write personalized applications for better understanding and optimizations of their workloads and systems.
This commit is implementing only the stub for the initialization, basic data structures, and simple manipulation functions of the structures. The core mechanisms of DAMON will be implemented by following commits.
--- include/linux/damon.h | 66 ++++++++++++++++ mm/Kconfig | 11 +++ mm/Makefile | 1 + mm/damon.c | 176 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 include/linux/damon.h create mode 100644 mm/damon.c
include/linux/damon.h new file +66 -0
@@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* DAMON api +* +* Copyright 2019-2020 Amazon.com, Inc. or its affiliates. +* +* Author: SeongJae Park <[email protected]> +*/ + +#ifndef _DAMON_H_ +#define _DAMON_H_ + +#include <linux/random.h> +#include <linux/types.h> + +/** +* struct damon_addr_range - Represents an address region of [@start, @end). +* @start: Start address of the region (inclusive). +* @end: End address of the region (exclusive). +*/ +struct damon_addr_range { +unsigned long start; +unsigned long end; +}; + +/** +* struct damon_region - Represents a monitoring target region. +* @ar: The address range of the region. +* @sampling_addr: Address of the sample for the next access check. +* @nr_accesses: Access frequency of this region. +* @list: List head for siblings. +*/ +struct damon_region { +struct damon_addr_range ar; +unsigned long sampling_addr; +unsigned int nr_accesses; +struct list_head list; +}; + +/** +* struct damon_target - Represents a monitoring target. +* @id: Unique identifier for this target. +* @regions_list: Head of the monitoring target regions of this target. +* @list: List head for siblings. +* +* Each monitoring context could have multiple targets. For example, a context +* for virtual memory address spaces could have multiple target processes. The +* @id of each target should be unique among the targets of the context. For +* example, in the virtual address monitoring context, it could be a pidfd or +* an address of an mm_struct. +*/ +struct damon_target { +unsigned long id; +struct list_head regions_list; +struct list_head list; +}; + +/** +* struct damon_ctx - Represents a context for each monitoring. +* @targets_list: Head of monitoring targets (&damon_target) list. +*/ +struct damon_ctx { +struct list_head targets_list;/* 'damon_target' objects */ +}; + +#endif
mm/Kconfig +11 -0
@@ -872,4 +872,15 @@ config ARCH_HAS_HUGEPD config MAPPING_DIRTY_HELPERS bool +config DAMON + bool "Data Access Monitor" + help + This feature allows to monitor access frequency of each memory + region. The information can be useful for performance-centric DRAM + level memory management. + + See https://damonitor.github.io/doc/html/latest-damon/index.html for + more information. + If unsure, say N. + endmenu
mm/Makefile +1 -0
@@ -121,3 +121,4 @@ obj-$(CONFIG_MEMFD_CREATE) += memfd.o obj-$(CONFIG_MAPPING_DIRTY_HELPERS) += mapping_dirty_helpers.o obj-$(CONFIG_PTDUMP_CORE) += ptdump.o obj-$(CONFIG_PAGE_REPORTING) += page_reporting.o +obj-$(CONFIG_DAMON) += damon.o
mm/damon.c new file +176 -0
176 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v19 2/15 mm/damon: Implement region based sampling
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
DAMON separates its monitoring target address space independent high level logics from the target space dependent low level primitives for flexible support of various address spaces.
This commit implements DAMON's target address space independent high level logics for basic access check and region based sampling. Hence, without the target address space specific parts implementations, this doesn't work alone. A reference implementation of those will be provided by a later commit.
Basic Access Check
==================
The output of DAMON says what pages are how frequently accessed for a given duration. The resolution of the access frequency is controlled by setting ``sampling interval`` and ``aggregation interval``. In detail, DAMON checks access to each page per ``sampling interval`` and aggregates the results. In other words, counts the number of the accesses to each page. After each ``aggregation interval`` passes, DAMON calls callback functions that previously registered by users so that users can read the aggregated results and then clears the results. This can be described in below simple pseudo-code::
while monitoring_on:
for page in monitoring_target:
if accessed(page):
nr_accesses[page] += 1
if time() % aggregation_interval == 0:
for callback in user_registered_callbacks:
callback(monitoring_target, nr_accesses)
for page in monitoring_target:
nr_accesses[page] = 0
sleep(sampling interval)
The monitoring overhead of this mechanism will arbitrarily increase as the size of the target workload grows.
Region Based Sampling
=====================
To avoid the unbounded increase of the overhead, DAMON groups adjacent pages that assumed to have the same access frequencies into a region. As long as the assumption (pages in a region have the same access frequencies) is kept, only one page in the region is required to be checked. Thus, for each ``sampling interval``, DAMON randomly picks one page in each region, waits for one ``sampling interval``, checks whether the page is accessed meanwhile, and increases the access frequency of the region if so. Therefore, the monitoring overhead is controllable by setting the number of regions. DAMON allows users to set the minimum and the maximum number of regions for the trade-off.
This scheme, however, cannot preserve the quality of the output if the assumption is not guaranteed. Next commit will address this problem.
--- include/linux/damon.h | 89 ++++++++++++++- mm/damon.c | 256 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 342 insertions(+), 3 deletions(-)
include/linux/damon.h +88 -1
@@ -11,6 +11,8 @@#define _DAMON_H_#include <linux/random.h> +#include <linux/mutex.h> +#include <linux/time64.h> #include <linux/types.h> /**
@@ -56,11 +58,96 @@ struct damon_target { };/** -* struct damon_ctx - Represents a context for each monitoring. +* struct damon_ctx - Represents a context for each monitoring. This is the +* main interface that allows users to set the attributes and get the results +* of the monitoring. +* +* @sample_interval: The time between access samplings. +* @aggr_interval: The time between monitor results aggregations. +* @nr_regions: The number of monitoring regions. +* +* For each @sample_interval, DAMON checks whether each region is accessed or +* not. It aggregates and keeps the access information (number of accesses to +* each region) for @aggr_interval time. All time intervals are in +* micro-seconds. +* +* @kdamond: Kernel thread who does the monitoring. +* @kdamond_stop: Notifies whether kdamond should stop. +* @kdamond_lock: Mutex for the synchronizations with @kdamond. +* +* For each monitoring request (damon_start()), a kernel thread for the +* monitoring is created. The pointer to the thread is stored in @kdamond. +* +* Once started, the monitoring thread runs until explicitly required to be +* terminated or every monitoring target is invalid. The validity of the +* targets is checked via the @target_valid callback. The termination can also +* be explicitly requested by writing non-zero to @kdamond_stop. The thread +* sets @kdamond to NULL when it terminates. Therefore, users can know whether +* the monitoring is ongoing or terminated by reading @kdamond. Reads and +* writes to @kdamond and @kdamond_stop from outside of the monitoring thread +* must be protected by @kdamond_lock. +* +* Note that the monitoring thread protects only @kdamond and @kdamond_stop via +* @kdamond_lock. Accesses to other fields must be protected by themselves. +* * @targets_list: Head of monitoring targets (&damon_target) list. +* +* @init_target_regions: Constructs initial monitoring target regions. +* @prepare_access_checks: Prepares next access check of target regions. +* @check_accesses: Checks the access of target regions. +* @target_valid: Determine if the target is valid. +* @sample_cb: Called for each sampling interval. +* @aggregate_cb: Called for each aggregation interval. +* +* DAMON can be extended for various address spaces by users. For this, users +* can register the target address space dependent low level functions for +* their usecases via the callback pointers of the context. The monitoring +* thread calls @init_target_regions before starting the monitoring, and +* @prepare_access_checks, @check_accesses, and @target_valid for each +* @sample_interval. +* +* @init_target_regions should construct proper monitoring target regions and +* link those to the DAMON context struct. +* @prepare_access_checks should manipulate the monitoring regions to be +* prepare for the next access check. +* @check_accesses should check the accesses to each region that made after the +* last preparation and update the `->nr_accesses` of each region. +* @target_valid should check whether the target is still valid for the +* monitoring. +* +* @sample_cb and @aggregate_cb are called from @kdamond for each of the +* sampling intervals and aggregation intervals, respectively. Therefore, +* users can safely access to the monitoring results via @targets_list without +* additional protection of @kdamond_lock. For the reason, users are +* recommended to use these callback for the accesses to the results. */ struct damon_ctx { +unsigned long sample_interval; +unsigned long aggr_interval; +unsigned long nr_regions; + +struct timespec64 last_aggregation; + +struct task_struct *kdamond; +bool kdamond_stop; +struct mutex kdamond_lock; +struct list_head targets_list;/* 'damon_target' objects */ + +/* callbacks */ +void (*init_target_regions)(struct damon_ctx *context); +void (*prepare_access_checks)(struct damon_ctx *context); +unsigned int (*check_accesses)(struct damon_ctx *context); +bool (*target_valid)(struct damon_target *target); +void (*sample_cb)(struct damon_ctx *context); +void (*aggregate_cb)(struct damon_ctx *context); }; +int damon_set_targets (struct damon_ctx *ctx, +unsigned long *ids,ssize_t nr_ids); +int damon_set_attrs (struct damon_ctx *ctx,unsigned long sample_int, +unsigned long aggr_int,unsigned long min_nr_reg); +int damon_start (struct damon_ctx *ctx); +int damon_stop (struct damon_ctx *ctx); +#endif
mm/damon.c +254 -2
278 lines not on this page · raw message
Signature · 1 line
2.17.1
Re: [PATCH v19 02/15] mm/damon: Implement region based sampling
Quoted 11 lines · On Tue, 4 Aug 2020 11:14:03 +0200 SeongJae Park <[email protected]> wrote:
From: SeongJae Park <[email protected]>
DAMON separates its monitoring target address space independent high level logics from the target space dependent low level primitives for flexible support of various address spaces.
This commit implements DAMON's target address space independent high level logics for basic access check and region based sampling. Hence, without the target address space specific parts implementations, this doesn't work alone. A reference implementation of those will be provided by a later commit.
[...]
Quoted 34 lines
Signed-off-by SeongJae Park <[email protected]>Reviewed-by Leonard Foerster <[email protected]>--- include/linux/damon.h | 89 ++++++++++++++- mm/damon.c | 256 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 342 insertions(+), 3 deletions(-)include/linux/damon.h +11
-1@@ -11,6 +11,8 @@#define _DAMON_H_#include <linux/random.h> +#include <linux/mutex.h> +#include <linux/time64.h> #include <linux/types.h> /** @@ -56,11 +58,96 @@ struct damon_target { };/** -* struct damon_ctx - Represents a context for each monitoring. +* struct damon_ctx - Represents a context for each monitoring. This is the +* main interface that allows users to set the attributes and get the results +* of the monitoring. +* +* @sample_interval: The time between access samplings. +* @aggr_interval: The time between monitor results aggregations. +* @nr_regions: The number of monitoring regions. +* +* For each @sample_interval, DAMON checks whether each region is accessed or
include/linux/damon.h struct damon_target { · line 79
Quoted 10 lines
include/linux/damon.h +10
-0+* not. It aggregates and keeps the access information (number of accesses to +* each region) for @aggr_interval time. All time intervals are in +* micro-seconds. +* +* @kdamond: Kernel thread who does the monitoring. +* @kdamond_stop: Notifies whether kdamond should stop. +* @kdamond_lock: Mutex for the synchronizations with @kdamond. +* +* For each monitoring request (damon_start()), a kernel thread for the +* monitoring is created. The pointer to the thread is stored in @kdamond.
This means that multiple monitoring threads can concurrently run. This is an intended design to let users utilize multiple CPUs. For example, let's suppose the user need super high accuracy of the monitoring results which require multiple CPU power. If use of the multiple CPUs are allowed, the user can split the monitoring target regions into multiple contexts and call 'damon_start()' for each of the context.
If multiple monitoring threads has conflicting target regions, they will interfere each other. Currently, avoidance of such conflict should be done by 'damon_start()' users. The synchronization would be complicated.
To make the situation simple, I will make the 'damon_start()' receive a group of 'damon_ctx' objects and creates a group of monitoring threads for the objects at once. This will only reduce number of 'damon_start()' calls required to run multiple monitoring threads. In addition to this, the groups of monitoring threads will be mutual exclusive. In other words, 'damon_start()' will fail if a group of monitoring threads that started by other 'damon_start()' call is currently running.
This still ask the users to protect each monitoring threads by taking care in their requests. But, because the requests are made by the user on its own, avoiding the conflict will be quite easy. Further, intentional conflict is also possible, though I'm unsure how the intentional conflict can be required.
The change will be made in the next spin.
Thanks,
SeongJae Park
[...]
PATCH v19 3/15 mm/damon: Adaptively adjust regions
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
Even somehow the initial monitoring target regions are well constructed to fulfill the assumption (pages in same region have similar access frequencies), the data access pattern can be dynamically changed. This will result in low monitoring quality. To keep the assumption as much as possible, DAMON adaptively merges and splits each region based on their access frequency.
For each ``aggregation interval``, it compares the access frequencies of adjacent regions and merges those if the frequency difference is small. Then, after it reports and clears the aggregated access frequency of each region, it splits each region into two or three regions if the total number of regions will not exceed the user-specified maximum number of regions after the split.
In this way, DAMON provides its best-effort quality and minimal overhead while keeping the upper-bound overhead that users set.
--- include/linux/damon.h | 11 ++- mm/damon.c | 191 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 189 insertions(+), 13 deletions(-)
include/linux/damon.h +7 -4
@@ -64,7 +64,8 @@ struct damon_target {* * @sample_interval: The time between access samplings. * @aggr_interval: The time between monitor results aggregations. -* @nr_regions: The number of monitoring regions. +* @min_nr_regions: The minimum number of monitoring regions. +* @max_nr_regions: The maximum number of monitoring regions. * * For each @sample_interval, DAMON checks whether each region is accessed or * not. It aggregates and keeps the access information (number of accesses to
@@ -124,7 +125,8 @@ struct damon_target {struct damon_ctx {unsigned long sample_interval;unsigned long aggr_interval; -unsigned long nr_regions; +unsigned long min_nr_regions; +unsigned long max_nr_regions;struct timespec64 last_aggregation;
@@ -145,8 +147,9 @@ struct damon_ctx {int damon_set_targets (struct damon_ctx *ctx,unsigned long *ids,ssize_t nr_ids); -int damon_set_attrs (struct damon_ctx *ctx,unsigned long sample_int, -unsigned long aggr_int,unsigned long min_nr_reg); +int damon_set_attrs (struct damon_ctx *ctx, +unsigned long sample_int,unsigned long aggr_int, +unsigned long min_nr_reg,unsigned long max_nr_reg);int damon_start (struct damon_ctx *ctx);int damon_stop (struct damon_ctx *ctx);
mm/damon.c +182 -9
243 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v19 4/15 mm/damon: Track dynamic monitoring target regions update
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
The monitoring target address range can be dynamically changed. For example, virtual memory could be dynamically mapped and unmapped. Physical memory could be hot-plugged.
As the changes could be quite frequent in some cases, DAMON checks the dynamic memory mapping changes and applies it to the abstracted target area only for each of a user-specified time interval, ``regions update interval``.
--- include/linux/damon.h | 20 +++++++++++++++----- mm/damon.c | 23 +++++++++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-)
include/linux/damon.h +15 -5
@@ -64,13 +64,16 @@ struct damon_target {* * @sample_interval: The time between access samplings. * @aggr_interval: The time between monitor results aggregations. +* @regions_update_interval: The time between monitor regions updates. * @min_nr_regions: The minimum number of monitoring regions. * @max_nr_regions: The maximum number of monitoring regions. * * For each @sample_interval, DAMON checks whether each region is accessed or * not. It aggregates and keeps the access information (number of accesses to -* each region) for @aggr_interval time. All time intervals are in -* micro-seconds. +* each region) for @aggr_interval time. DAMON also checks whether the target +* memory regions need update (e.g., by ``mmap()`` calls from the application, +* in case of virtual memory monitoring) and applies the changes for each +* @regions_update_interval. All time intervals are in micro-seconds. * * @kdamond: Kernel thread who does the monitoring. * @kdamond_stop: Notifies whether kdamond should stop.
@@ -94,6 +97,7 @@ struct damon_target {* @targets_list: Head of monitoring targets (&damon_target) list. * * @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. * @check_accesses: Checks the access of target regions. * @target_valid: Determine if the target is valid.
@@ -103,12 +107,15 @@ struct damon_target {* DAMON can be extended for various address spaces by users. For this, users * can register the target address space dependent low level functions for * their usecases via the callback pointers of the context. The monitoring -* thread calls @init_target_regions before starting the monitoring, and +* thread calls @init_target_regions before starting the monitoring, +* @update_target_regions for each @regions_update_interval, and * @prepare_access_checks, @check_accesses, and @target_valid for each * @sample_interval. * * @init_target_regions should construct proper monitoring target regions and * link those to the DAMON context struct. +* @update_target_regions should update the monitoring target regions for +* current status. * @prepare_access_checks should manipulate the monitoring regions to be * prepare for the next access check. * @check_accesses should check the accesses to each region that made after the
@@ -125,10 +132,12 @@ struct damon_target {struct damon_ctx {unsigned long sample_interval;unsigned long aggr_interval; +unsigned long regions_update_interval;unsigned long min_nr_regions;unsigned long max_nr_regions;struct timespec64 last_aggregation; +struct timespec64 last_regions_update;struct task_struct *kdamond;bool kdamond_stop;
@@ -138,6 +147,7 @@ struct damon_ctx {/* callbacks */ void (*init_target_regions)(struct damon_ctx *context); +void (*update_target_regions)(struct damon_ctx *context);void (*prepare_access_checks)(struct damon_ctx *context);unsigned int (*check_accesses)(struct damon_ctx *context);bool (*target_valid)(struct damon_target *target);
@@ -147,8 +157,8 @@ struct damon_ctx {int damon_set_targets (struct damon_ctx *ctx,unsigned long *ids,ssize_t nr_ids); -int damon_set_attrs (struct damon_ctx *ctx, -unsigned long sample_int,unsigned long aggr_int, +int damon_set_attrs (struct damon_ctx *ctx,unsigned long sample_int, +unsigned long aggr_int,unsigned long regions_update_int,unsigned long min_nr_reg,unsigned long max_nr_reg);int damon_start (struct damon_ctx *ctx);int damon_stop (struct damon_ctx *ctx);
mm/damon.c +21 -2
@@ -382,6 +382,17 @@ static void kdamond_split_regions(struct damon_ctx *ctx) last_nr_regions = nr_regions; } +/* +* Check whether it is time to check and apply the target monitoring regions +* +* Returns true if it is. +*/ +static bool kdamond_need_update_regions (struct damon_ctx *ctx) +{ +return damon_check_reset_time_interval (&ctx->last_regions_update, + ctx->regions_update_interval); +} +/* * Check whether current monitoring should be stopped *
@@ -447,6 +458,12 @@ static int kdamond_fn(void *data)kdamond_reset_aggregated (ctx);kdamond_split_regions (ctx); } + +if (kdamond_need_update_regions (ctx)) { +if (ctx->update_target_regions) + ctx->update_target_regions (ctx); + sz_limit =damon_region_sz_limit (ctx); + } }damon_for_each_target (t, ctx) {damon_for_each_region_safe (r, next, t)
@@ -557,6 +574,7 @@ int damon_set_targets(struct damon_ctx *ctx,* damon_set_attrs() - Set attributes for the monitoring. * @ctx: monitoring context * @sample_int: time interval between samplings +* @regions_update_int: time interval between target regions update * @aggr_int: time interval between aggregations * @min_nr_reg: minimal number of regions * @max_nr_reg: maximum number of regions
@@ -566,8 +584,8 @@ int damon_set_targets(struct damon_ctx *ctx,* * Return: 0 on success, negative error code otherwise. */ -int damon_set_attrs (struct damon_ctx *ctx, -unsigned long sample_int,unsigned long aggr_int, +int damon_set_attrs (struct damon_ctx *ctx,unsigned long sample_int, +unsigned long aggr_int,unsigned long regions_update_int,unsigned long min_nr_reg,unsigned long max_nr_reg) {if (min_nr_reg <3 ) {
@@ -583,6 +601,7 @@ int damon_set_attrs(struct damon_ctx *ctx, ctx->sample_interval = sample_int; ctx->aggr_interval = aggr_int; + ctx->regions_update_interval = regions_update_int; ctx->min_nr_regions = min_nr_reg; ctx->max_nr_regions = max_nr_reg;
Signature · 1 line
2.17.1
PATCH v19 5/15 mm/idle_page_tracking: Make PG_(idle|young) reusable
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
PG_idle and PG_young allows the two PTE Accessed bit users, IDLE_PAGE_TRACKING and the reclaim logic concurrently work while don't interfere each other. That is, when they need to clear the Accessed bit, they set PG_young and PG_idle to represent the previous state of the bit, respectively. And when they need to read the bit, if the bit is cleared, they further read the PG_young and PG_idle, respectively, to know whether the other has cleared the bit meanwhile or not.
We could add another page flag and extend the mechanism to use the flag if we need to add another concurrent PTE Accessed bit user subsystem. However, it would be only waste the space. Instead, if the new subsystem is mutually exclusive with IDLE_PAGE_TRACKING, it could simply reuse the PG_idle flag. However, it's impossible because the flags are dependent on IDLE_PAGE_TRACKING.
To allow such reuse of the flags, this commit separates the PG_young and PG_idle flag logic from IDLE_PAGE_TRACKING and introduces new kernel config, 'PAGE_IDLE_FLAG'. Hence, if !IDLE_PAGE_TRACKING and IDLE_PAGE_FLAG, a new subsystem would be able to reuse PG_idle.
In the next commit, DAMON's reference implementation of the virtual memory address space monitoring primitives will use it.
--- include/linux/page-flags.h | 4 ++-- include/linux/page_ext.h | 2 +- include/linux/page_idle.h | 6 +++--- include/trace/events/mmflags.h | 2 +- mm/Kconfig | 8 ++++++++ mm/page_ext.c | 12 +++++++++++- mm/page_idle.c | 10 ---------- 7 files changed, 26 insertions(+), 18 deletions(-)
include/linux/page-flags.h +2 -2
@@ -132,7 +132,7 @@ enum pageflags {#ifdef CONFIG_MEMORY_FAILURE PG_hwpoison, /* hardware poisoned page. Don't touch */#endif -#if defined(CONFIG_IDLE_PAGE_TRACKING ) && defined(CONFIG_64BIT ) +#if defined(CONFIG_PAGE_IDLE_FLAG ) && defined(CONFIG_64BIT ) PG_young, PG_idle,#endif
@@ -432,7 +432,7 @@ static inline bool set_hwpoison_free_buddy_page(struct page *page)#define __PG_HWPOISON 0#endif -#if defined(CONFIG_IDLE_PAGE_TRACKING ) && defined(CONFIG_64BIT ) +#if defined(CONFIG_PAGE_IDLE_FLAG ) && defined(CONFIG_64BIT ) TESTPAGEFLAG(Young, young, PF_ANY) SETPAGEFLAG(Young, young, PF_ANY)TESTCLEARFLAG (Young, young,PF_ANY )
include/linux/page_ext.h +1 -1
@@ -19,7 +19,7 @@ struct page_ext_operations {enum page_ext_flags {PAGE_EXT_OWNER ,PAGE_EXT_OWNER_ALLOCATED , -#if defined(CONFIG_IDLE_PAGE_TRACKING ) && !defined(CONFIG_64BIT ) +#if defined(CONFIG_PAGE_IDLE_FLAG ) && !defined(CONFIG_64BIT )PAGE_EXT_YOUNG ,PAGE_EXT_IDLE ,#endif
include/linux/page_idle.h +3 -3
@@ -6,7 +6,7 @@#include <linux/page-flags.h> #include <linux/page_ext.h> -#ifdef CONFIG_IDLE_PAGE_TRACKING +#ifdef CONFIG_PAGE_IDLE_FLAG #ifdef CONFIG_64BIT static inline bool page_is_young (struct page *page)
@@ -106,7 +106,7 @@ static inline void clear_page_idle(struct page *page) }#endif /* CONFIG_64BIT */ -#else /* !CONFIG_IDLE_PAGE_TRACKING */ +#else /* !CONFIG_PAGE_IDLE_FLAG */ static inline bool page_is_young (struct page *page) {
@@ -135,6 +135,6 @@ static inline void clear_page_idle(struct page *page) { } -#endif /* CONFIG_IDLE_PAGE_TRACKING */ +#endif /* CONFIG_PAGE_IDLE_FLAG */ #endif /* _LINUX_MM_PAGE_IDLE_H */
include/trace/events/mmflags.h +1 -1
@@ -73,7 +73,7 @@#define IF_HAVE_PG_HWPOISON (flag,string)#endif -#if defined(CONFIG_IDLE_PAGE_TRACKING ) && defined(CONFIG_64BIT ) +#if defined(CONFIG_PAGE_IDLE_FLAG ) && defined(CONFIG_64BIT )#define IF_HAVE_PG_IDLE (flag,string) ,{1UL << flag, string}#else #define IF_HAVE_PG_IDLE (flag,string)
mm/Kconfig +8 -0
@@ -765,10 +765,18 @@ config DEFERRED_STRUCT_PAGE_INIT lifetime of the system until these kthreads finish the initialisation. +config PAGE_IDLE_FLAG + bool "Add PG_idle and PG_young flags" + help + This feature adds PG_idle and PG_young flags in 'struct page'. PTE + Accessed bit writers can set the state of the bit in the flags to let + other PTE Accessed bit readers don't disturbed. + config IDLE_PAGE_TRACKING bool "Enable idle page tracking" depends on SYSFS && MMU select PAGE_EXTENSION if !64BIT + select PAGE_IDLE_FLAG help This feature allows to estimate the amount of user pages that have not been touched during a given period of time. This information can
mm/page_ext.c +11 -1
@@ -58,11 +58,21 @@* can utilize this callback to initialize the state of it correctly. */ +#if defined(CONFIG_PAGE_IDLE_FLAG ) && !defined(CONFIG_64BIT ) +static bool need_page_idle (void ) +{ +return true; +} +struct page_ext_operations page_idle_ops = { + .need = need_page_idle, +}; +#endif +static struct page_ext_operations *page_ext_ops[] = { #ifdef CONFIG_PAGE_OWNER &page_owner_ops, #endif -#if defined(CONFIG_IDLE_PAGE_TRACKING) && !defined(CONFIG_64BIT) +#if defined(CONFIG_PAGE_IDLE_FLAG) && !defined(CONFIG_64BIT) &page_idle_ops, #endif };
mm/page_idle.c +0 -10
@@ -211,16 +211,6 @@ static const struct attribute_group page_idle_attr_group = { .name = "page_idle", }; -#ifndef CONFIG_64BIT -static bool need_page_idle (void ) -{ -return true; -} -struct page_ext_operations page_idle_ops = { - .need = need_page_idle, -}; -#endif -static int __init page_idle_init (void ) {int err;
Signature · 1 line
2.17.1
PATCH v19 6/15 mm/damon: Implement callbacks for the virtual memory address spaces
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit introduces a reference implementation of the address space specific low level primitives for the virtual address space, so that users of DAMON can easily monitor the data accesses on virtual address spaces of specific processes by simply configuring the implementation to be used by DAMON.
The low level primitives for the fundamental access monitoring are defined in two parts:
1. Identification of the monitoring target address range for the address space.
2. Access check of specific address range in the target space.
The reference implementation for the virtual address space provided by this commit is designed as below.
PTE Accessed-bit Based Access Check
-----------------------------------
The implementation uses PTE Accessed-bit for basic access checks. That is, it clears the bit for next sampling target page and checks whether it set again after one sampling period. This could disturb other kernel subsystems using the Accessed bits, namely Idle page tracking and the reclaim logic. To avoid such disturbances, DAMON makes it mutually exclusive with Idle page tracking and uses ``PG_idle`` and ``PG_young`` page flags to solve the conflict with the reclaim logics, as Idle page tracking does.
VMA-based Target Address Range Construction
-------------------------------------------
Only small parts in the super-huge virtual address space of the processes are mapped to physical memory and accessed. Thus, tracking the unmapped address regions is just wasteful. However, because DAMON can deal with some level of noise using the adaptive regions adjustment mechanism, tracking every mapping is not strictly required but could even incur a high overhead in some cases. That said, too huge unmapped areas inside the monitoring target should be removed to not take the time for the adaptive mechanism.
For the reason, this implementation converts the complex mappings to three distinct regions that cover every mapped area of the address space. Also, the two gaps between the three regions are the two biggest unmapped areas in the given address space. The two biggest unmapped areas would be the gap between the heap and the uppermost mmap()-ed region, and the gap between the lowermost mmap()-ed region and the stack in most of the cases. Because these gaps are exceptionally huge in usual address spacees, excluding these will be sufficient to make a reasonable trade-off. Below shows this in detail::
<heap>
<BIG UNMAPPED REGION 1>
<uppermost mmap()-ed region>
(small mmap()-ed regions and munmap()-ed regions) <lowermost mmap()-ed region>
<BIG UNMAPPED REGION 2>
<stack>
--- include/linux/damon.h | 7 + mm/Kconfig | 3 + mm/damon.c | 542 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 552 insertions(+)
include/linux/damon.h +7 -0
@@ -155,6 +155,13 @@ struct damon_ctx {void (*aggregate_cb)(struct damon_ctx *context); }; +/* Reference callback implementations for virtual memory */ +void kdamond_init_vm_regions (struct damon_ctx *ctx); +void kdamond_update_vm_regions (struct damon_ctx *ctx); +void kdamond_prepare_vm_access_checks (struct damon_ctx *ctx); +unsigned int kdamond_check_vm_accesses (struct damon_ctx *ctx); +bool kdamond_vm_target_valid (struct damon_target *t); +int damon_set_targets (struct damon_ctx *ctx,unsigned long *ids,ssize_t nr_ids); int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int,
mm/Kconfig +3 -0
@@ -882,6 +882,9 @@ config MAPPING_DIRTY_HELPERS config DAMON bool "Data Access Monitor" + depends on MMU && !IDLE_PAGE_TRACKING + select PAGE_EXTENSION if !64BIT + select PAGE_IDLE_FLAG help This feature allows to monitor access frequency of each memory region. The information can be useful for performance-centric DRAM
mm/damon.c +542 -0
560 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v19 7/15 mm/damon: Implement access pattern recording
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit implements the recording feature of DAMON. If this feature is enabled, DAMON writes the monitored access patterns in its binary format into a file which specified by the user. This is already able to be implemented by each user using the callbacks. However, as the recording is expected to be widely used, this commit implements the feature in the DAMON, for more convenience.
--- include/linux/damon.h | 15 +++++ mm/damon.c | 141 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 3 deletions(-)
include/linux/damon.h +15 -0
@@ -75,6 +75,14 @@ struct damon_target {* 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.
@@ -139,6 +147,11 @@ 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;
@@ -167,6 +180,8 @@ int damon_set_targets(struct damon_ctx *ctx,int damon_set_attrs (struct damon_ctx *ctx,unsigned long sample_int,unsigned long aggr_int,unsigned long regions_update_int,unsigned long min_nr_reg,unsigned long max_nr_reg); +int damon_set_recording (struct damon_ctx *ctx, +unsigned int rbuf_len,char *rfile_path);int damon_start (struct damon_ctx *ctx);int damon_stop (struct damon_ctx *ctx);
mm/damon.c +138 -3
184 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v19 8/15 mm/damon: Add a tracepoint
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit adds a tracepoint for DAMON. It traces the monitoring results of each region for each aggregation interval. Using this, DAMON can easily integrated with tracepoints supporting tools such as perf.
--- include/trace/events/damon.h | 43 ++++++++++++++++++++++++++++++++++++ mm/damon.c | 4 ++++ 2 files changed, 47 insertions(+) create mode 100644 include/trace/events/damon.h
include/trace/events/damon.h new file +43 -0
@@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM damon + +#if !defined(_TRACE_DAMON_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _TRACE_DAMON_H + +#include <linux/damon.h> +#include <linux/types.h> +#include <linux/tracepoint.h> + +TRACE_EVENT (damon_aggregated, + + TP_PROTO(struct damon_target *t, struct damon_region *r, + unsigned int nr_regions), + +TP_ARGS (t, r, nr_regions), + +TP_STRUCT__entry ( + __field(unsigned long, target_id) + __field(unsigned int, nr_regions) + __field(unsigned long, start) + __field(unsigned long, end) + __field(unsigned int, nr_accesses) + ), + +TP_fast_assign ( + __entry->target_id = t->id; + __entry->nr_regions = nr_regions; + __entry->start = r->ar.start; + __entry->end = r->ar.end; + __entry->nr_accesses = r->nr_accesses; + ), + +TP_printk ("target_id=%lu nr_regions=%u %lu-%lu: %u" , + __entry->target_id, __entry->nr_regions, + __entry->start, __entry->end, __entry->nr_accesses) +); + +#endif /* _TRACE_DAMON_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h>
mm/damon.c +4 -0
@@ -20,6 +20,8 @@#define pr_fmt (fmt) "damon: " fmt +#define CREATE_TRACE_POINTS +#include <linux/damon.h> #include <linux/delay.h> #include <linux/kthread.h>
@@ -31,6 +33,7 @@#include <linux/sched/mm.h> #include <linux/sched/task.h> #include <linux/slab.h> +#include <trace/events/damon.h> /* Minimal region size. Every damon_region is aligned by this. */ #define MIN_REGION PAGE_SIZE
@@ -856,6 +859,7 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)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->nr_accesses =0 ; } }
Signature · 1 line
2.17.1
Re: [PATCH v19 08/15] mm/damon: Add a tracepoint
Quoted 77 lines · On Tue, 4 Aug 2020 11:14:09 +0200 SeongJae Park <[email protected]> wrote:
From: SeongJae Park <[email protected]>
This commit adds a tracepoint for DAMON. It traces the monitoring results of each region for each aggregation interval. Using this, DAMON can easily integrated with tracepoints supporting tools such as perf.
Signed-off-by SeongJae Park <[email protected]>Reviewed-by Leonard Foerster <[email protected]>--- include/trace/events/damon.h | 43 ++++++++++++++++++++++++++++++++++++ mm/damon.c | 4 ++++ 2 files changed, 47 insertions(+) create mode 100644 include/trace/events/damon.hinclude/trace/events/damon.h new file +43
-0@@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM damon + +#if !defined(_TRACE_DAMON_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _TRACE_DAMON_H + +#include <linux/damon.h> +#include <linux/types.h> +#include <linux/tracepoint.h> + +TRACE_EVENT (damon_aggregated, + + TP_PROTO(struct damon_target *t, struct damon_region *r, + unsigned int nr_regions), + +TP_ARGS (t, r, nr_regions), + +TP_STRUCT__entry ( + __field(unsigned long, target_id) + __field(unsigned int, nr_regions) + __field(unsigned long, start) + __field(unsigned long, end) + __field(unsigned int, nr_accesses) + ), + +TP_fast_assign ( + __entry->target_id = t->id; + __entry->nr_regions = nr_regions; + __entry->start = r->ar.start; + __entry->end = r->ar.end; + __entry->nr_accesses = r->nr_accesses; + ), + +TP_printk ("target_id=%lu nr_regions=%u %lu-%lu: %u" , + __entry->target_id, __entry->nr_regions, + __entry->start, __entry->end, __entry->nr_accesses) +); + +#endif /* _TRACE_DAMON_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> mm/damon.c +2
-0@@ -20,6 +20,8 @@#define pr_fmt (fmt) "damon: " fmt +#define CREATE_TRACE_POINTS +#include <linux/damon.h> #include <linux/delay.h> #include <linux/kthread.h>
mm/damon.c line 35
Quoted 3 lines
mm/damon.c +0
-0@@ -31,6 +33,7 @@#include <linux/sched/mm.h> #include <linux/sched/task.h> #include <linux/slab.h>
It's best to place the #define CREATE_TRACE_POINTS here, so that it doesn't cause any side effects when including the other headers.
Other than that:
-- Steve
Quoted 12 lines
mm/damon.c +2
-0+#include <trace/events/damon.h> /* Minimal region size. Every damon_region is aligned by this. */ #define MIN_REGION PAGE_SIZE@@ -856,6 +859,7 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)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->nr_accesses =0 ; } }
Re: [PATCH v19 08/15] mm/damon: Add a tracepoint
Quoted 17 lines · On Tue, 4 Aug 2020 18:01:47 -0400 Steven Rostedt <[email protected]> wrote:
Quoted 14 lines · On Tue, 4 Aug 2020 11:14:09 +0200 SeongJae Park <[email protected]> wrote:
From: SeongJae Park <[email protected]>
This commit adds a tracepoint for DAMON. It traces the monitoring results of each region for each aggregation interval. Using this, DAMON can easily integrated with tracepoints supporting tools such as perf.
Signed-off-by SeongJae Park <[email protected]>Reviewed-by Leonard Foerster <[email protected]>--- include/trace/events/damon.h | 43 ++++++++++++++++++++++++++++++++++++ mm/damon.c | 4 ++++ 2 files changed, 47 insertions(+) create mode 100644 include/trace/events/damon.h
[...]
Quoted 6 lines
Quoted 6 lines
mm/damon.c +0
-0@@ -20,6 +20,8 @@#define pr_fmt (fmt) "damon: " fmt
Quoted 12 lines
mm/damon.c line 35
Quoted 9 lines
mm/damon.c +2
-0+#define CREATE_TRACE_POINTS +#include <linux/damon.h> #include <linux/delay.h> #include <linux/kthread.h> @@ -31,6 +33,7 @@#include <linux/sched/mm.h> #include <linux/sched/task.h> #include <linux/slab.h> It's best to place the #define
CREATE_TRACE_POINTShere, so that it doesn't cause any side effects when including the other headers.
Agreed, I will do so in the next spin!
Quoted 6 lines
Other than that:
Reviewed-by Steven Rostedt (VMware) <[email protected]>-- Steve
Thanks,
SeongJae Park
PATCH v19 9/15 mm/damon: Implement a debugfs interface
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit implements a debugfs interface for DAMON. It works for the virtual address spaces monitoring.
DAMON exports four files, ``attrs``, ``target_ids``, ``record``, and ``monitor_on`` under its debugfs directory, ``<debugfs>/damon/``.
Attributes
----------
Users can read and write the ``sampling interval``, ``aggregation interval``, ``regions update interval``, and min/max number of monitoring target regions by reading from and writing to the ``attrs`` file. For example, below commands set those values to 5 ms, 100 ms, 1,000 ms, 10, 1000 and check it again::
# cd <debugfs>/damon
# echo 5000 100000 1000000 10 1000 > attrs
# cat attrs
5000 100000 1000000 10 1000Target IDs
----------
Some types of address spaces supports multiple monitoring target. For example, the virtual memory address spaces monitoring can have multiple processes as the monitoring targets. Users can set the targets by writing relevant id values of the targets to, and get the ids of the current targets by reading from the ``target_ids`` file. In case of the virtual address spaces monitoring, the values should be pids of the monitoring target processes. For example, below commands set processes having pids 42 and 4242 as the monitoring targets and check it again::
# cd <debugfs>/damon
# echo 42 4242 > target_ids
# cat target_ids
42 4242Note that setting the target ids doesn't start the monitoring.
Record
------
DAMON supports direct monitoring result record feature. The recorded results are first written to a buffer and flushed to a file in batch. Users can 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.dataTurning On/Off
--------------
Setting the files as described above doesn't incur effect unless you explicitly start the monitoring. You can start, stop, and check the current status of the monitoring by writing to and reading from the ``monitor_on`` file. Writing ``on`` to the file starts the monitoring of the targets with the attributes. Writing ``off`` to the file stops those. DAMON also stops if every targets are invalidated (in case of the virtual memory monitoring, target processes are invalidated when terminated). Below example commands turn on, off, and check the status of DAMON::
# cd <debugfs>/damon
# echo on > monitor_on
# echo off > monitor_on
# cat monitor_on
offPlease note that you cannot write to the above-mentioned debugfs files while the monitoring is turned on. If you write to the files while DAMON is running, an error code such as ``-EBUSY`` will be returned.
--- mm/damon.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 395 insertions(+), 1 deletion(-)
mm/damon.c +395 -1
@@ -15,6 +15,7 @@* - Functions for the target validity check * - Functions for DAMON core logics and features * - Functions for the DAMON programming interface +* - Functions for the DAMON debugfs interface * - Functions for the initialization */
@@ -23,6 +24,7 @@#define CREATE_TRACE_POINTS #include <linux/damon.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/kthread.h> #include <linux/mm.h>
@@ -67,6 +69,21 @@/* Get a random number in [l, r) */ #define damon_rand (l, r) (l + prandom_u32() % (r - l)) +/* A monitoring context for debugfs interface users. */ +static struct damon_ctx damon_user_ctx = { + .sample_interval =5 *1000 , + .aggr_interval =100 *1000 , + .regions_update_interval =1000 *1000 , + .min_nr_regions =10 , + .max_nr_regions =1000 , + + .init_target_regions = kdamond_init_vm_regions, + .update_target_regions = kdamond_update_vm_regions, + .prepare_access_checks = kdamond_prepare_vm_access_checks, + .check_accesses = kdamond_check_vm_accesses, + .target_valid = kdamond_vm_target_valid, +}; +/* * Construct a damon_region struct *
@@ -1289,13 +1306,390 @@ int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int,return 0 ; } +/* +* Functions for the DAMON debugfs interface +*/ + +static ssize_t debugfs_monitor_on_read (struct file *file, +char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +char monitor_on_buf[5 ]; +bool monitor_on; +int len; + + monitor_on =damon_kdamond_running (ctx); + len =snprintf (monitor_on_buf,5 , monitor_on ?"on\n" :"off\n" ); + +return simple_read_from_buffer (buf, count, ppos, monitor_on_buf, len); +} + +/* +* Returns non-empty string on success, negarive error code otherwise. +*/ +static char *user_input_str (const char __user *buf,size_t count,loff_t *ppos) +{ +char *kbuf; +ssize_t ret; + +/* We do not accept continuous write */ +if (*ppos) +return ERR_PTR (-EINVAL ); + + kbuf =kmalloc (count +1 ,GFP_KERNEL ); +if (!kbuf) +return ERR_PTR (-ENOMEM ); + + ret =simple_write_to_buffer (kbuf, count +1 , ppos, buf, count); +if (ret != count) { +kfree (kbuf); +return ERR_PTR (-EIO ); + } + kbuf[ret] ='\0' ; + +return kbuf; +} + +static ssize_t debugfs_monitor_on_write (struct file *file, +const char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +ssize_t ret = count; +char *kbuf; +int err; + + kbuf =user_input_str (buf, count, ppos); +if (IS_ERR (kbuf)) +return PTR_ERR (kbuf); + +/* Remove white space */ +if (sscanf (kbuf,"%s" , kbuf) !=1 ) +return -EINVAL ; +if (!strncmp (kbuf,"on" , count)) + err =damon_start (ctx); +else if (!strncmp (kbuf,"off" , count)) + err =damon_stop (ctx); +else +return -EINVAL ; + +if (err) + ret = err; +return ret; +} + +#define targetid_is_pid (ctx) \ + (ctx->target_valid == kdamond_vm_target_valid) + +static ssize_t sprint_target_ids (struct damon_ctx *ctx,char *buf,ssize_t len) +{ +struct damon_target *t; +unsigned long id; +int written =0 ; +int rc; + +damon_for_each_target (t, ctx) { + id = t->id; +if (targetid_is_pid (ctx)) +/* Show pid numbers to debugfs users */ + id = (unsigned long )pid_vnr ((struct pid *)id); + + rc =snprintf (&buf[written], len - written,"%lu " , id); +if (!rc) +return -ENOMEM ; + written += rc; + } +if (written) + written -=1 ; + written +=snprintf (&buf[written], len - written,"\n" ); +return written; +} + +static ssize_t debugfs_target_ids_read (struct file *file, +char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +ssize_t len; +char ids_buf[320 ]; + +mutex_lock (&ctx->kdamond_lock); + len =sprint_target_ids (ctx, ids_buf,320 ); +mutex_unlock (&ctx->kdamond_lock); +if (len <0 ) +return len; + +return simple_read_from_buffer (buf, count, ppos, ids_buf, len); +} + +/* +* Converts a string into an array of unsigned long integers +* +* Returns an array of unsigned long integers if the conversion success, or +* NULL otherwise. +*/ +static unsigned long *str_to_target_ids (const char *str,ssize_t len, +ssize_t *nr_ids) +{ +unsigned long *ids; +const int max_nr_ids =32 ; +unsigned long id; +int pos =0 , parsed, ret; + + *nr_ids =0 ; + ids =kmalloc_array (max_nr_ids,sizeof (id),GFP_KERNEL ); +if (!ids) +return NULL ; +while (*nr_ids < max_nr_ids && pos < len) { + ret =sscanf (&str[pos],"%lu%n" , &id, &parsed); + pos += parsed; +if (ret !=1 ) +break ; + ids[*nr_ids] = id; + *nr_ids +=1 ; + } + +return ids; +} + +static ssize_t debugfs_target_ids_write (struct file *file, +const char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +char *kbuf; +unsigned long *targets; +ssize_t nr_targets; +ssize_t ret = count; +struct damon_target *target; +int i; +int err; + + kbuf =user_input_str (buf, count, ppos); +if (IS_ERR (kbuf)) +return PTR_ERR (kbuf); + + targets =str_to_target_ids (kbuf, ret, &nr_targets); +if (!targets) { + ret = -ENOMEM ; + goto out; + } + +if (targetid_is_pid (ctx)) { +for (i =0 ; i < nr_targets; i++) + targets[i] = (unsigned long )find_get_pid ( + (int )targets[i]); + } + +mutex_lock (&ctx->kdamond_lock); +if (ctx->kdamond) { + ret = -EINVAL ; + goto unlock_out; + } + +if (targetid_is_pid (ctx)) { +damon_for_each_target (target, ctx) +put_pid ((struct pid *)target->id); + } + + err =damon_set_targets (ctx, targets, nr_targets); +if (err) + ret = err; +unlock_out: +mutex_unlock (&ctx->kdamond_lock); +kfree (targets); +out: +kfree (kbuf); +return ret; +} + +static ssize_t debugfs_record_read (struct file *file, +char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +char record_buf[20 +MAX_RFILE_PATH_LEN ]; +int ret; + +mutex_lock (&ctx->kdamond_lock); + ret =snprintf (record_buf,ARRAY_SIZE (record_buf),"%u %s\n" , + ctx->rbuf_len, ctx->rfile_path); +mutex_unlock (&ctx->kdamond_lock); +return simple_read_from_buffer (buf, count, ppos, record_buf, ret); +} + +static ssize_t debugfs_record_write (struct file *file, +const char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +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 =damon_set_recording (ctx, rbuf_len, rfile_path); +if (err) + ret = err; +unlock_out: +mutex_unlock (&ctx->kdamond_lock); +out: +kfree (kbuf); +return ret; +} + +static ssize_t debugfs_attrs_read (struct file *file, +char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +char kbuf[128 ]; +int ret; + +mutex_lock (&ctx->kdamond_lock); + ret =snprintf (kbuf,ARRAY_SIZE (kbuf),"%lu %lu %lu %lu %lu\n" , + ctx->sample_interval, ctx->aggr_interval, + ctx->regions_update_interval, ctx->min_nr_regions, + ctx->max_nr_regions); +mutex_unlock (&ctx->kdamond_lock); + +return simple_read_from_buffer (buf, count, ppos, kbuf, ret); +} + +static ssize_t debugfs_attrs_write (struct file *file, +const char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +unsigned long s, a, r, minr, maxr; +char *kbuf; +ssize_t ret = count; +int err; + + kbuf =user_input_str (buf, count, ppos); +if (IS_ERR (kbuf)) +return PTR_ERR (kbuf); + +if (sscanf (kbuf,"%lu %lu %lu %lu %lu" , + &s, &a, &r, &minr, &maxr) !=5 ) { + ret = -EINVAL ; + goto out; + } + +mutex_lock (&ctx->kdamond_lock); +if (ctx->kdamond) { + ret = -EBUSY ; + goto unlock_out; + } + + err =damon_set_attrs (ctx, s, a, r, minr, maxr); +if (err) + ret = err; +unlock_out: +mutex_unlock (&ctx->kdamond_lock); +out: +kfree (kbuf); +return ret; +} + +static const struct file_operations monitor_on_fops = { + .owner =THIS_MODULE , + .read = debugfs_monitor_on_read, + .write = debugfs_monitor_on_write, +}; + +static const struct file_operations target_ids_fops = { + .owner =THIS_MODULE , + .read = debugfs_target_ids_read, + .write = debugfs_target_ids_write, +}; + +static const struct file_operations record_fops = { + .owner =THIS_MODULE , + .read = debugfs_record_read, + .write = debugfs_record_write, +}; + +static const struct file_operations attrs_fops = { + .owner =THIS_MODULE , + .read = debugfs_attrs_read, + .write = debugfs_attrs_write, +}; + +static struct dentry *debugfs_root; + +static int __init damon_debugfs_init (void ) +{ +const char *const file_names[] = {"attrs" ,"record" , +"target_ids" ,"monitor_on" }; +const struct file_operations *fops[] = {&attrs_fops, &record_fops, + &target_ids_fops, &monitor_on_fops}; +int i; + + debugfs_root =debugfs_create_dir ("damon" ,NULL ); +if (!debugfs_root) { +pr_err ("failed to create the debugfs dir\n" ); +return -ENOMEM ; + } + +for (i =0 ; i <ARRAY_SIZE (file_names); i++) { +if (!debugfs_create_file (file_names[i],0600 , debugfs_root, +NULL , fops[i])) { +pr_err ("failed to create %s file\n" , file_names[i]); +return -ENOMEM ; + } + } + +return 0 ; +} + +static int __init damon_init_user_ctx (void ) +{ +int rc; + +struct damon_ctx *ctx = &damon_user_ctx; + +ktime_get_coarse_ts64 (&ctx->last_aggregation); + ctx->last_regions_update = ctx->last_aggregation; + + rc =damon_set_recording (ctx,1024 *1024 ,"/damon.data" ); +if (rc) +return rc; + +mutex_init (&ctx->kdamond_lock); + +INIT_LIST_HEAD (&ctx->targets_list); + +return 0 ; +} +/* * Functions for the initialization */ static int __init damon_init (void ) { -return 0 ; +int rc; + + rc =damon_init_user_ctx (); +if (rc) +return rc; + + rc =damon_debugfs_init (); +if (rc) +pr_err ("%s: debugfs init failed\n" , __func__); + +return rc; }module_init (damon_init);
Signature · 1 line
2.17.1
Re: [PATCH v19 09/15] mm/damon: Implement a debugfs interface
Quoted 4 lines · On Tue, 4 Aug 2020 11:14:10 +0200 SeongJae Park <[email protected]> wrote:
From: SeongJae Park <[email protected]>
This commit implements a debugfs interface for DAMON. It works for the virtual address spaces monitoring.
[...]
Quoted 4 lines
mm/damon.c +4
-0+ +#define targetid_is_pid (ctx) \ + (ctx->target_valid == kdamond_vm_target_valid) +
[...]
Quoted 28 lines
mm/damon.c +28
-0+ +static ssize_t debugfs_target_ids_write (struct file *file, +const char __user *buf,size_t count,loff_t *ppos) +{ +struct damon_ctx *ctx = &damon_user_ctx; +char *kbuf; +unsigned long *targets; +ssize_t nr_targets; +ssize_t ret = count; +struct damon_target *target; +int i; +int err; + + kbuf =user_input_str (buf, count, ppos); +if (IS_ERR (kbuf)) +return PTR_ERR (kbuf); + + targets =str_to_target_ids (kbuf, ret, &nr_targets); +if (!targets) { + ret = -ENOMEM ; + goto out; + } + +if (targetid_is_pid (ctx)) { +for (i =0 ; i < nr_targets; i++) + targets[i] = (unsigned long )find_get_pid ( + (int )targets[i]); + }
mm/damon.c int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int, · line 1489
Quoted 10 lines
mm/damon.c +10
-0+ +mutex_lock (&ctx->kdamond_lock); +if (ctx->kdamond) { + ret = -EINVAL ; + goto unlock_out; + } + +if (targetid_is_pid (ctx)) { +damon_for_each_target (target, ctx) +put_pid ((struct pid *)target->id);
If non-pid target ids were set before by the kernel API, this will cause a problem. Therefore, the DAMON users should cleanup there target ids properly. However, I found that this could be easily missed. Indeed, my new test code missed the cleanup. Moreover, it would be hard to do that when concurrent DAMON users exist.
One straightforward fix would be making 'damon_set_targets()' to remember last target id type and do 'put_pid()' if the last target id type was pid, instead of here. This will work, but make the address space independent part to be coupled with the dependent part.
Or, we could add another callback for cleanup and let debugfs code to register a function doing 'put_pid()' and remove of the targets as the callback. This approach will allow the address space independent part to be remain independent.
I will fix this problem with the second approach in the next spin.
Thanks,
SeongJae Park
PATCH v19 10/15 damon/debugfs: Support pidfd target id
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
DAMON programming interface users are safe from pid recycling problem since it uses 'struct pid *' as target id. However, debugfs interface users might still get the problem since they should describe the target ids with pid numbers.
This commit makes the debugfs interface to support pidfd target ids. Using it, debugfs interface users can also easily be safe from the races. That said, the pid numbers are stil supported for the simple command line uses.
The pid numbers target ids usages are same to the old one. Therefore, old users don't need to change anything. To use pidfd target ids, users should add a prefix, 'pidfd ' in front of the numbers. Because pidfd is process private, reading 'target_ids' file again will show pid numbers of the target processes.
--- mm/damon.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-)
mm/damon.c +34 -3
@@ -1450,11 +1450,32 @@ static unsigned long *str_to_target_ids(const char *str, ssize_t len,return ids; } +/* Returns pid for the given pidfd if it's valid, or NULL otherwise. */ +static struct pid *damon_get_pidfd_pid (unsigned int pidfd) +{ +struct fd f; +struct pid *pid; + + f =fdget (pidfd); +if (!f.file) +return NULL ; + + pid =pidfd_pid (f.file); +if (!IS_ERR (pid)) +get_pid (pid); +else + pid =NULL ; + +fdput (f); +return pid; +} +static ssize_t debugfs_target_ids_write (struct file *file,const char __user *buf,size_t count,loff_t *ppos) {struct damon_ctx *ctx = &damon_user_ctx; -char *kbuf; +char *kbuf, *nrs; +bool received_pidfds = false;unsigned long *targets;ssize_t nr_targets;ssize_t ret = count;
@@ -1466,13 +1487,23 @@ static ssize_t debugfs_target_ids_write(struct file *file,if (IS_ERR (kbuf))return PTR_ERR (kbuf); - targets =str_to_target_ids (kbuf, ret, &nr_targets); + nrs = kbuf; +if (!strncmp (kbuf,"pidfd " ,6 )) { + received_pidfds = true; + nrs = &kbuf[6 ]; + } + + targets =str_to_target_ids (nrs, ret, &nr_targets);if (!targets) { ret = -ENOMEM ; goto out; } -if (targetid_is_pid (ctx)) { +if (received_pidfds) { +for (i =0 ; i < nr_targets; i++) + targets[i] = (unsigned long )damon_get_pidfd_pid ( + (unsigned int )targets[i]); + }else if (targetid_is_pid (ctx)) {for (i =0 ; i < nr_targets; i++) targets[i] = (unsigned long )find_get_pid ( (int )targets[i]);
Signature · 1 line
2.17.1
PATCH v19 11/15 tools: Introduce a minimal user-space tool for DAMON
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit imtroduces a shallow wrapper python script, ``/tools/damon/damo`` that provides more convenient interface. Note that it is only aimed to be used for minimal reference of the DAMON's debugfs interfaces and for debugging of the DAMON itself.
--- tools/damon/.gitignore | 1 + tools/damon/_damon.py | 130 ++++++++++++++ tools/damon/_dist.py | 36 ++++ tools/damon/_recfile.py | 23 +++ tools/damon/bin2txt.py | 67 +++++++ tools/damon/damo | 37 ++++ tools/damon/heats.py | 362 ++++++++++++++++++++++++++++++++++++++ tools/damon/nr_regions.py | 91 ++++++++++ tools/damon/record.py | 135 ++++++++++++++ tools/damon/report.py | 45 +++++ tools/damon/wss.py | 100 +++++++++++ 11 files changed, 1027 insertions(+) create mode 100644 tools/damon/.gitignore create mode 100644 tools/damon/_damon.py create mode 100644 tools/damon/_dist.py create mode 100644 tools/damon/_recfile.py create mode 100644 tools/damon/bin2txt.py create mode 100755 tools/damon/damo create mode 100644 tools/damon/heats.py create mode 100644 tools/damon/nr_regions.py create mode 100644 tools/damon/record.py create mode 100644 tools/damon/report.py create mode 100644 tools/damon/wss.py
tools/damon/.gitignore new file +1 -0
@@ -0,0 +1 @@ +__pycache__/*
tools/damon/_damon.py new file +130 -0
130 lines not on this page · raw message
tools/damon/_dist.py new file +36 -0
@@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import os +import struct +import subprocess + +def access_patterns(f): + nr_regions = struct.unpack('I', f.read(4))[0] + + patterns = [] + for r in range(nr_regions): + saddr = struct.unpack('L', f.read(8))[0] + eaddr = struct.unpack('L', f.read(8))[0] + nr_accesses = struct.unpack('I', f.read(4))[0] + patterns.append([eaddr - saddr, nr_accesses]) + return patterns + +def plot_dist(data_file, output_file, xlabel, ylabel): + terminal = output_file.split('.')[-1] + if not terminal in ['pdf', 'jpeg', 'png', 'svg']: + os.remove(data_file) + print("Unsupported plot output type.") + exit(-1) + + gnuplot_cmd = """ + set term %s; + set output '%s'; + set key off; + set xlabel '%s'; + set ylabel '%s'; + plot '%s' with linespoints;""" % (terminal, output_file, xlabel, ylabel, + data_file) + subprocess.call(['gnuplot', '-e', gnuplot_cmd]) + os.remove(data_file) +
tools/damon/_recfile.py new file +23 -0
@@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import struct + +fmt_version = 0 + +def set_fmt_version(f): + global fmt_version + + mark = f.read(16) + if mark == b'damon_recfmt_ver': + fmt_version = struct.unpack('i', f.read(4))[0] + else: + fmt_version = 0 + f.seek(0) + return fmt_version + +def target_id(f): + if fmt_version == 1: + return struct.unpack('i', f.read(4))[0] + else: + return struct.unpack('L', f.read(8))[0]
tools/damon/bin2txt.py new file +67 -0
67 lines not on this page · raw message
tools/damon/damo new file +37 -0
@@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +import argparse + +import record +import report + +class SubCmdHelpFormatter(argparse.RawDescriptionHelpFormatter): + def _format_action(self, action): + parts = super(argparse.RawDescriptionHelpFormatter, + self)._format_action(action) + # skip sub parsers help + if action.nargs == argparse.PARSER: + parts = '\n'.join(parts.split('\n')[1:]) + return parts + +parser = argparse.ArgumentParser(formatter_class=SubCmdHelpFormatter) + +subparser = parser.add_subparsers(title='command', dest='command', + metavar='<command>') +subparser.required = True + +parser_record = subparser.add_parser('record', + help='record data accesses of the given target processes') +record.set_argparser(parser_record) + +parser_report = subparser.add_parser('report', + help='report the recorded data accesses in the specified form') +report.set_argparser(parser_report) + +args = parser.parse_args() + +if args.command == 'record': + record.main(args) +elif args.command == 'report': + report.main(args)
tools/damon/heats.py new file +362 -0
362 lines not on this page · raw message
tools/damon/nr_regions.py new file +91 -0
91 lines not on this page · raw message
tools/damon/record.py new file +135 -0
135 lines not on this page · raw message
tools/damon/report.py new file +45 -0
45 lines not on this page · raw message
tools/damon/wss.py new file +100 -0
100 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v19 12/15 Documentation: Add documents for DAMON
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit adds documents for DAMON under Documentation/admin-guide/mm/damon/ and Documentation/vm/damon/.
--- Documentation/admin-guide/mm/damon/guide.rst | 157 ++++++++++ Documentation/admin-guide/mm/damon/index.rst | 15 + Documentation/admin-guide/mm/damon/plans.rst | 29 ++ Documentation/admin-guide/mm/damon/start.rst | 96 ++++++ Documentation/admin-guide/mm/damon/usage.rst | 302 +++++++++++++++++++ Documentation/admin-guide/mm/index.rst | 1 + Documentation/vm/damon/api.rst | 20 ++ Documentation/vm/damon/design.rst | 166 ++++++++++ Documentation/vm/damon/eval.rst | 222 ++++++++++++++ Documentation/vm/damon/faq.rst | 58 ++++ Documentation/vm/damon/index.rst | 31 ++ Documentation/vm/index.rst | 1 + 12 files changed, 1098 insertions(+) create mode 100644 Documentation/admin-guide/mm/damon/guide.rst create mode 100644 Documentation/admin-guide/mm/damon/index.rst create mode 100644 Documentation/admin-guide/mm/damon/plans.rst create mode 100644 Documentation/admin-guide/mm/damon/start.rst create mode 100644 Documentation/admin-guide/mm/damon/usage.rst create mode 100644 Documentation/vm/damon/api.rst create mode 100644 Documentation/vm/damon/design.rst create mode 100644 Documentation/vm/damon/eval.rst create mode 100644 Documentation/vm/damon/faq.rst create mode 100644 Documentation/vm/damon/index.rst
Documentation/admin-guide/mm/damon/guide.rst new file +157 -0
@@ -0,0 +1,157 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================== +Optimization Guide +================== + +This document helps you estimating the amount of benefit that you could get +from DAMON-based optimizations, and describes how you could achieve it. You +are assumed to already read :doc:`start`. + + +Check The Signs +=============== + +No optimization can provide same extent of benefit to every case. Therefore +you should first guess how much improvements you could get using DAMON. If +some of below conditions match your situation, you could consider using DAMON. + +- *Low IPC and High Cache Miss Ratios.* Low IPC means most of the CPU time is + spent waiting for the completion of time-consuming operations such as memory + access, while high cache miss ratios mean the caches don't help it well. + DAMON is not for cache level optimization, but DRAM level. However, + improving DRAM management will also help this case by reducing the memory + operation latency. +- *Memory Over-commitment and Unknown Users.* If you are doing memory + overcommitment and you cannot control every user of your system, a memory + bank run could happen at any time. You can estimate when it will happen + based on DAMON's monitoring results and act earlier to avoid or deal better + with the crisis. +- *Frequent Memory Pressure.* Frequent memory pressure means your system has + wrong configurations or memory hogs. DAMON will help you find the right + configuration and/or the criminals. +- *Heterogeneous Memory System.* If your system is utilizing memory devices + that placed between DRAM and traditional hard disks, such as non-volatile + memory or fast SSDs, DAMON could help you utilizing the devices more + efficiently. + + +Profile +======= + +If you found some positive signals, you could start by profiling your workloads +using DAMON. Find major workloads on your systems and analyze their data +access pattern to find something wrong or can be improved. The DAMON user +space tool (``damo``) will be useful for this. + +We recommend you to start from working set size distribution check using ``damo +report wss``. If the distribution is ununiform or quite different from what +you estimated, you could consider `Memory Configuration`_ optimization. + +Then, review the overall access pattern in heatmap form using ``damo report +heats``. If it shows a simple pattern consists of a small number of memory +regions having high contrast of access temperature, you could consider manual +`Program Modification`_. + +If you still want to absorb more benefits, you should develop `Personalized +DAMON Application`_ for your special case. + +You don't need to take only one approach among the above plans, but you could +use multiple of the above approaches to maximize the benefit. + + +Optimize +======== + +If the profiling result also says it's worth trying some optimization, you +could consider below approaches. Note that some of the below approaches assume +that your systems are configured with swap devices or other types of auxiliary +memory so that you don't strictly required to accommodate the whole working set +in the main memory. Most of the detailed optimization should be made on your +concrete understanding of your memory devices. + + +Memory Configuration +-------------------- + +No more no less, DRAM should be large enough to accommodate only important +working sets, because DRAM is highly performance critical but expensive and +heavily consumes the power. However, knowing the size of the real important +working sets is difficult. As a consequence, people usually equips +unnecessarily large or too small DRAM. Many problems stem from such wrong +configurations. + +Using the working set size distribution report provided by ``damo report wss``, +you can know the appropriate DRAM size for you. For example, roughly speaking, +if you worry about only 95 percentile latency, you don't need to equip DRAM of +a size larger than 95 percentile working set size. + +Let's see a real example. This `page +<https://damonitor.github.io/doc/html/v17/admin-guide/mm/damon/guide.html#memory-configuration>`_ +shows the heatmap and the working set size distributions/changes of +``freqmine`` workload in PARSEC3 benchmark suite. The working set size spikes +up to 180 MiB, but keeps smaller than 50 MiB for more than 95% of the time. +Even though you give only 50 MiB of memory space to the workload, it will work +well for 95% of the time. Meanwhile, you can save the 130 MiB of memory space. + + +Program Modification +-------------------- + +If the data access pattern heatmap plotted by ``damo report heats`` is quite +simple so that you can understand how the things are going in the workload with +your human eye, you could manually optimize the memory management. + +For example, suppose that the workload has two big memory object but only one +object is frequently accessed while the other one is only occasionally +accessed. Then, you could modify the program source code to keep the hot +object in the main memory by invoking ``mlock()`` or ``madvise()`` with +``MADV_WILLNEED``. Or, you could proactively evict the cold object using +``madvise()`` with ``MADV_COLD`` or ``MADV_PAGEOUT``. Using both together +would be also worthy. + +A research work [1]_ using the ``mlock()`` achieved up to 2.55x performance +speedup. + +Let's see another realistic example access pattern for this kind of +optimizations. This `page +<https://damonitor.github.io/doc/html/v17/admin-guide/mm/damon/guide.html#program-modification>`_ +shows the visualized access patterns of streamcluster workload in PARSEC3 +benchmark suite. We can easily identify the 100 MiB sized hot object. + + +Personalized DAMON Application +------------------------------ + +Above approaches will work well for many general cases, but would not enough +for some special cases. + +If this is the case, it might be the time to forget the comfortable use of the +user space tool and dive into the debugfs interface (refer to :doc:`usage` for +the detail) of DAMON. Using the interface, you can control the DAMON more +flexibly. Therefore, you can write your personalized DAMON application that +controls the monitoring via the debugfs interface, analyzes the result, and +applies complex optimizations itself. Using this, you can make more creative +and wise optimizations. + +If you are a kernel space programmer, writing kernel space DAMON applications +using the API (refer to the :doc:`/vm/damon/api` for more detail) would be an +option. + + +Reference Practices +=================== + +Referencing previously done successful practices could help you getting the +sense for this kind of optimizations. There is an academic paper [1]_ +reporting the visualized access pattern and manual `Program +Modification`_ results for a number of realistic workloads. You can also get +the visualized access patterns [3]_ [4]_ [5]_ and automated DAMON-based memory +operations results for other realistic workloads that collected with latest +version of DAMON [2]_ . + +.. [1] https://dl.acm.org/doi/10.1145/3366626.3368125 +.. [2] https://damonitor.github.io/test/result/perf/latest/html/ +.. [3] https://damonitor.github.io/test/result/visual/latest/rec.heatmap.1.png.html +.. [4] https://damonitor.github.io/test/result/visual/latest/rec.wss_sz.png.html +.. [5] https://damonitor.github.io/test/result/visual/latest/rec.wss_time.png.html
Documentation/admin-guide/mm/damon/index.rst new file +15 -0
@@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================== +Monitoring Data Accesses +======================== + +:doc:`DAMON </vm/damon/index>` allows light-weight data access monitoring. +Using this, users can analyze and optimize their systems. + +.. toctree:: + :maxdepth: 2 + + start + guide + usage
Documentation/admin-guide/mm/damon/plans.rst new file +29 -0
@@ -0,0 +1,29 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============ +Future Plans +============ + +DAMON is still on its first stage. Below plans are still under development. + + +Automate Data Access Monitoring-based Memory Operation Schemes Execution +======================================================================== + +The ultimate goal of DAMON is to be used as a building block for the data +access pattern aware kernel memory management optimization. It will make +system just works efficiently. However, some users having very special +workloads will want to further do their own optimization. DAMON will automate +most of the tasks for such manual optimizations in near future. Users will be +required to only describe what kind of data access pattern-based operation +schemes they want in a simple form. + +By applying a very simple scheme for THP promotion/demotion with a prototype +implementation, DAMON reduced 60% of THP memory footprint overhead while +preserving 50% of the THP performance benefit. The detailed results can be +seen on an external web page [1]_. + +Several RFC patchsets for this plan are available [2]_. + +.. [1] https://damonitor.github.io/test/result/perf/latest/html/ +.. [2] https://lore.kernel.org/linux-mm/[email protected]/
Documentation/admin-guide/mm/damon/start.rst new file +96 -0
96 lines not on this page · raw message
Documentation/admin-guide/mm/damon/usage.rst new file +302 -0
302 lines not on this page · raw message
Documentation/admin-guide/mm/index.rst +1 -0
@@ -27,6 +27,7 @@ the Linux memory management. concepts cma_debugfs + damon/index hugetlbpage idle_page_tracking ksm
Documentation/vm/damon/api.rst new file +20 -0
@@ -0,0 +1,20 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============= +API Reference +============= + +Kernel space programs can use every feature of DAMON using below APIs. All you +need to do is including ``damon.h``, which is located in ``include/linux/`` of +the source tree. + +Structures +========== + +.. kernel-doc:: include/linux/damon.h + + +Functions +========= + +.. kernel-doc:: mm/damon.c
Documentation/vm/damon/design.rst new file +166 -0
166 lines not on this page · raw message
Documentation/vm/damon/eval.rst new file +222 -0
222 lines not on this page · raw message
Documentation/vm/damon/faq.rst new file +58 -0
58 lines not on this page · raw message
Documentation/vm/damon/index.rst new file +31 -0
@@ -0,0 +1,31 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +DAMON: Data Access MONitor +========================== + +DAMON is a data access monitoring framework subsystem for the Linux kernel. +The core mechanisms of DAMON (refer to :doc:`design` for the detail) make it + + - *accurate* (the monitoring output is useful enough for DRAM level memory + management; It might not appropriate for CPU Cache levels, though), + - *light-weight* (the monitoring overhead is low enough to be applied online), + and + - *scalable* (the upper-bound of the overhead is in constant range regardless + of the size of target workloads). + +Using this framework, therefore, the kernel's memory management mechanisms can +make advanced decisions. Experimental memory management optimization works +that incurring high data accesses monitoring overhead could implemented again. +In user space, meanwhile, users who have some special workloads can write +personalized applications for better understanding and optimizations of their +workloads and systems. + +.. toctree:: + :maxdepth: 2 + + faq + design + eval + api + plans
Documentation/vm/index.rst +1 -0
@@ -31,6 +31,7 @@ descriptions of data structures and algorithms. active_mm balance cleancache + damon/index free_page_reporting frontswap highmem
Signature · 1 line
2.17.1
PATCH v19 13/15 mm/damon: Add kunit tests
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit adds kunit based unit tests for DAMON.
--- mm/Kconfig | 11 + mm/damon-test.h | 671 ++++++++++++++++++++++++++++++++++++++++++++++++ mm/damon.c | 6 + 3 files changed, 688 insertions(+) create mode 100644 mm/damon-test.h
mm/Kconfig +11 -0
@@ -894,4 +894,15 @@ config DAMON more information. If unsure, say N. +config DAMON_KUNIT_TEST + bool "Test for damon" + depends on DAMON=y && KUNIT + help + This builds the DAMON Kunit test suite. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation. + + If unsure, say N. + endmenu
mm/damon-test.h new file +671 -0
671 lines not on this page · raw message
mm/damon.c +6 -0
@@ -38,7 +38,11 @@#include <trace/events/damon.h> /* Minimal region size. Every damon_region is aligned by this. */ +#ifndef CONFIG_DAMON_KUNIT_TEST #define MIN_REGION PAGE_SIZE +#else +#define MIN_REGION 1 +#endif /* * Functions and macros for DAMON data structures
@@ -1724,3 +1728,5 @@ static int __init damon_init(void) }module_init (damon_init); + +#include "damon-test.h"
Signature · 1 line
2.17.1
PATCH v19 14/15 mm/damon: Add user space selftests
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit adds a simple user space tests for DAMON. The tests are using kselftest framework.
--- tools/testing/selftests/damon/Makefile | 7 + .../selftests/damon/_chk_dependency.sh | 28 +++ tools/testing/selftests/damon/_chk_record.py | 109 ++++++++++++ .../testing/selftests/damon/debugfs_attrs.sh | 161 ++++++++++++++++++ .../testing/selftests/damon/debugfs_record.sh | 50 ++++++ 5 files changed, 355 insertions(+) create mode 100644 tools/testing/selftests/damon/Makefile create mode 100644 tools/testing/selftests/damon/_chk_dependency.sh create mode 100644 tools/testing/selftests/damon/_chk_record.py create mode 100755 tools/testing/selftests/damon/debugfs_attrs.sh create mode 100755 tools/testing/selftests/damon/debugfs_record.sh
tools/testing/selftests/damon/Makefile new file +7 -0
@@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for damon selftests + +TEST_FILES = _chk_dependency.sh _chk_record_file.py +TEST_PROGS = debugfs_attrs.sh debugfs_record.sh + +include ../lib.mk
tools/testing/selftests/damon/_chk_dependency.sh new file +28 -0
@@ -0,0 +1,28 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Kselftest framework requirement - SKIP code is 4. +ksft_skip=4 + +DBGFS=/sys/kernel/debug/damon + +if [ $EUID -ne 0 ]; +then + echo "Run as root" + exit $ksft_skip +fi + +if [ ! -d $DBGFS ] +then + echo "$DBGFS not found" + exit $ksft_skip +fi + +for f in attrs record target_ids monitor_on +do + if [ ! -f "$DBGFS/$f" ] + then + echo "$f not found" + exit 1 + fi +done
tools/testing/selftests/damon/_chk_record.py new file +109 -0
109 lines not on this page · raw message
tools/testing/selftests/damon/debugfs_attrs.sh new file +161 -0
161 lines not on this page · raw message
tools/testing/selftests/damon/debugfs_record.sh new file +50 -0
50 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v19 15/15 MAINTAINERS: Update for DAMON
In reply to SeongJae ParkFrom: SeongJae Park <[email protected]>
This commit updates MAINTAINERS file for DAMON related files.
--- MAINTAINERS | 13 +++++++++++++ 1 file changed, 13 insertions(+)
MAINTAINERS +13 -0
@@ -4751,6 +4751,19 @@ F: net/ax25/ax25_out.c F: net/ax25/ax25_timer.c F: net/ax25/sysctl_net_ax25.c +DATA ACCESS MONITOR +M: SeongJae Park <[email protected]> +L: [email protected] +S: Maintained +F: Documentation/admin-guide/mm/damon/* +F: Documentation/vm/damon/* +F: include/linux/damon.h +F: include/trace/events/damon.h +F: mm/damon-test.h +F: mm/damon.c +F: tools/damon/* +F: tools/testing/selftests/damon/* + DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER L: [email protected] S: Orphan
Signature · 1 line
2.17.1