| # SPDX-License-Identifier: GPL-2.0 |
| |
| """ |
| Contains core functions for DAMON sysfs control. |
| """ |
| |
| import os |
| import time |
| |
| import _damo_fmt_str |
| import _damo_fs |
| import _damon |
| import _damon_features |
| |
| sysfs_root = None |
| |
| def get_sysfs_root(): |
| global sysfs_root |
| |
| if sysfs_root is None: |
| sysfs_root = _damo_fs.dev_mount_point('sysfs') |
| return sysfs_root |
| |
| def get_kdamonds_dir(): |
| '''Returns None if sysfs is not mounted''' |
| if get_sysfs_root() is None: |
| return None |
| return os.path.join(get_sysfs_root(), 'kernel/mm/damon/admin/kdamonds') |
| |
| def supported(): |
| kdamonds_dir = get_kdamonds_dir() |
| if kdamonds_dir is None: |
| return False |
| return os.path.isdir(kdamonds_dir) |
| |
| def get_state_file_of(kdamond_idx): |
| return os.path.join(get_kdamonds_dir(), '%s' % kdamond_idx, 'state') |
| |
| def __write_state_file(kdamond_idxs, command): |
| 'Return error' |
| err = None |
| for kdamond_idx in kdamond_idxs: |
| err = _damo_fs.write_file(get_state_file_of(kdamond_idx), command) |
| if err != None: |
| break |
| return err |
| |
| def turn_damon_on(kdamonds_idxs): |
| # In case of vaddr, too early monitoring shows unstable mapping changes. |
| # Give the process a time to have stable memory mapping. |
| time.sleep(0.5) |
| return __write_state_file(kdamonds_idxs, 'on') |
| |
| def turn_damon_off(kdamonds_idxs): |
| return __write_state_file(kdamonds_idxs, 'off') |
| |
| def is_kdamond_running(kdamond_idx): |
| content, err = _damo_fs.read_file(get_state_file_of(kdamond_idx)) |
| if err != None: |
| print(err) |
| return False |
| return content.strip() == 'on' |
| |
| def get_refresh_ms_file_of(kdamond_idx): |
| return os.path.join(get_kdamonds_dir(), '%s' % kdamond_idx, 'refresh_ms') |
| |
| def refresh_ms_enabled(kdamond_idx): |
| filepath = get_refresh_ms_file_of(kdamond_idx) |
| if not os.path.isfile(filepath): |
| return False, None |
| content, err = _damo_fs.read_file(get_refresh_ms_file_of(kdamond_idx)) |
| if err is not None: |
| return False, err |
| try: |
| enabled = int(content) != 0 |
| except Exception as e: |
| return False, 'int(refresh_ms) failed (%s)! kernel bug?' % e |
| return enabled, None |
| |
| def refresh_ms_disabled_kdidxs(kdamond_idxs): |
| idxs = [] |
| for kdidx in kdamond_idxs: |
| unnecessary, err = refresh_ms_enabled(kdidx) |
| if err is not None: |
| return None, 'refresh_ms check fail (%s)' % err |
| if not unnecessary: |
| idxs.append(kdidx) |
| return idxs, None |
| |
| 'Return error' |
| def update_tuned_intervals(kdamond_idxs): |
| kdamond_idxs, err = refresh_ms_disabled_kdidxs(kdamond_idxs) |
| if err is not None: |
| return err |
| return __write_state_file(kdamond_idxs, 'update_tuned_intervals') |
| |
| 'Return error' |
| def update_schemes_stats(kdamond_idxs): |
| kdamond_idxs, err = refresh_ms_disabled_kdidxs(kdamond_idxs) |
| if err is not None: |
| return err |
| return __write_state_file(kdamond_idxs, 'update_schemes_stats') |
| |
| 'Return error' |
| def update_schemes_tried_bytes(kdamond_idxs): |
| err = __write_state_file(kdamond_idxs, 'update_schemes_tried_bytes') |
| if err != None: |
| err = '%s (maybe schemes_tried_regions_sz not supported?)' % err |
| return err |
| |
| 'Return error' |
| def update_schemes_tried_regions(kdamond_idxs): |
| return __write_state_file(kdamond_idxs, 'update_schemes_tried_regions') |
| |
| 'Return error' |
| def update_schemes_quota_effective_bytes(kdamond_idxs): |
| kdamond_idxs, err = refresh_ms_disabled_kdidxs(kdamond_idxs) |
| if err is not None: |
| return err |
| err = __write_state_file(kdamond_idxs, 'update_schemes_effective_quotas') |
| if err != None: |
| err = '%s (maybe schemes_effective_quotas not supported?)' % err |
| return err |
| |
| # for stage_kdamonds |
| |
| def write_filter_dir(dir_path, filter_): |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'type'), filter_.filter_type) |
| if err is not None: |
| # todo: make error message more detailed. |
| # anon/memcg are merged in 6.3-rc1 |
| # addr/target are merged in 6.6-rc1 |
| return err |
| |
| if filter_.allow is True: |
| if os.path.isfile(os.path.join(dir_path, 'allow')): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'allow'), |
| 'Y' if filter_.allow else 'N') |
| # pass file has renamed to allow during development |
| elif os.path.isfile(os.path.join(dir_path, 'pass')): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'pass'), |
| 'Y' if filter_.allow else 'N') |
| else: |
| return 'kernel is not supporting allow_filter' |
| if err is not None: |
| return err |
| |
| if filter_.memcg_path is not None: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'memcg_path'), filter_.memcg_path) |
| if err is not None: |
| return err |
| |
| if filter_.hugepage_size is not None: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'min'), |
| '%d' % filter_.hugepage_size[0]) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'max'), |
| '%d' % filter_.hugepage_size[1]) |
| if err is not None: |
| return err |
| |
| if filter_.address_range is not None: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'addr_start'), |
| '%d' % filter_.address_range.start) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'addr_end'), |
| '%d' % filter_.address_range.end) |
| if err is not None: |
| return err |
| |
| if filter_.damon_target_idx is not None: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'damon_target_idx'), |
| '%d' % filter_.damon_target_idx) |
| if err is not None: |
| return err |
| |
| return _damo_fs.write_file(os.path.join(dir_path, 'matching'), |
| 'Y' if filter_.matching else 'N') |
| |
| def ensure_nr_file_for(file_path, list_): |
| content, err = _damo_fs.read_file(file_path) |
| if err is not None: |
| return err |
| current_nr = int(content) |
| desired_nr = len(list_) |
| if current_nr == desired_nr: |
| return None |
| return _damo_fs.write_file(file_path, '%d' % desired_nr) |
| |
| def write_filters_dir(dir_path, filters): |
| # filters merged in v6.3-rc1 |
| if not os.path.isdir(dir_path): |
| if len(filters) == 0: |
| return None |
| return 'the kernel is not supporting filters' |
| |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_filters'), filters) |
| if err is not None: |
| return err |
| |
| for idx, filter_ in enumerate(filters): |
| err = write_filter_dir(os.path.join(dir_path, '%d' % idx), filter_) |
| if err is not None: |
| return err |
| return None |
| |
| def is_core_filter(damos_filter): |
| return damos_filter.filter_type in ['addr', 'target'] |
| |
| def write_core_ops_filters_dirs(scheme_dir_path, filters): |
| core_filters = [f for f in filters if is_core_filter(f)] |
| err = write_filters_dir(os.path.join(scheme_dir_path, 'core_filters'), |
| core_filters) |
| if err is not None: |
| return err |
| |
| ops_filters = [f for f in filters if not is_core_filter(f)] |
| err = write_filters_dir(os.path.join(scheme_dir_path, 'ops_filters'), |
| ops_filters) |
| if err is not None: |
| return err |
| |
| def write_filters_dirs(scheme_dir_path, filters): |
| if os.path.isdir(os.path.join(scheme_dir_path, 'core_filters')): |
| return write_core_ops_filters_dirs(scheme_dir_path, filters) |
| return write_filters_dir(os.path.join(scheme_dir_path, 'filters'), filters) |
| |
| def write_watermarks_dir(dir_path, wmarks): |
| if wmarks is None: |
| # TODO: ensure wmarks is not None |
| return None |
| err = _damo_fs.write_file(os.path.join(dir_path, 'metric'), wmarks.metric) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'interval_us'), '%d' % wmarks.interval_us) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'high'), '%d' % wmarks.high_permil) |
| if err is not None: |
| return err |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'mid'), '%d' % wmarks.mid_permil) |
| if err is not None: |
| return err |
| return _damo_fs.write_file( |
| os.path.join(dir_path, 'low'), '%d' % wmarks.low_permil) |
| |
| def write_quota_goal_dir(dir_path, goal): |
| # goal metric is wip as of 6.8-rc4 days. |
| if (not os.path.isfile(os.path.join(dir_path, 'target_metric')) and |
| goal.metric != 'user_input'): |
| return 'the kernel is not supporting quota goal metric' |
| |
| if os.path.isfile(os.path.join(dir_path, 'target_metric')): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'target_metric'), |
| '%s' % goal.metric) |
| if err is not None: |
| return err |
| |
| if goal.has_nid(): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'nid'), '%d' % goal.nid) |
| if err is not None: |
| return err |
| |
| if goal.has_memcg_path(): |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'path'), '%s' % goal.memcg_path) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'target_value'), |
| '%d' % goal.target_value) |
| if err is not None: |
| return err |
| |
| return _damo_fs.write_file( |
| os.path.join(dir_path, 'current_value'), |
| '%d' % goal.current_value) |
| |
| def write_quota_goals_dir(dir_path, goals): |
| # goals dir has merged in 6.8-rc1 |
| if not os.path.isdir(dir_path): |
| if len(goals) == 0: |
| return None |
| return 'the kernel is not supporting schemes quota goals' |
| |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_goals'), goals) |
| if err is not None: |
| return err |
| |
| for idx, goal in enumerate(goals): |
| err = write_quota_goal_dir(os.path.join(dir_path, '%d' % idx), goal) |
| if err is not None: |
| return err |
| return None |
| |
| def write_quota_weights_dir(dir_path, quotas): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'sz_permil'), |
| '%d' % quotas.weight_sz_permil) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file(os.path.join(dir_path, 'nr_accesses_permil'), |
| '%d' % quotas.weight_nr_accesses_permil) |
| if err is not None: |
| return err |
| |
| return _damo_fs.write_file(os.path.join(dir_path, 'age_permil'), |
| '%d' % quotas.weight_age_permil) |
| |
| def write_quotas_dir(dir_path, quotas): |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'ms'), '%d' % quotas.time_ms) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'bytes'), '%d' % quotas.sz_bytes) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file(os.path.join(dir_path, 'reset_interval_ms'), |
| '%d' % quotas.reset_interval_ms) |
| if err is not None: |
| return err |
| |
| err = write_quota_weights_dir(os.path.join(dir_path, 'weights'), quotas) |
| if err is not None: |
| return err |
| |
| return write_quota_goals_dir(os.path.join(dir_path, 'goals'), quotas.goals) |
| |
| def write_scheme_dests_dir(dir_path, dests): |
| if len(dests) == 0: |
| return None |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_dests'), dests) |
| if err is not None: |
| return err |
| |
| for idx, dest in enumerate(dests): |
| err = _damo_fs.write_file(os.path.join(dir_path, '%d' % idx, 'id'), |
| '%d' % dest.id) |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, '%d' % idx, 'weight'), |
| '%d' % dest.weight) |
| if err is not None: |
| return err |
| |
| def write_ulong(file_path, val): |
| return _damo_fs.write_file( |
| file_path, '%d' % min(val, _damo_fmt_str.ulong_max)) |
| |
| def write_scheme_access_pattern_dir(dir_path, pattern): |
| err = write_ulong( |
| os.path.join(dir_path, 'sz', 'min'), pattern.sz_bytes[0]) |
| if err is not None: |
| return err |
| |
| err = write_ulong( |
| os.path.join(dir_path, 'sz', 'max'), pattern.sz_bytes[1]) |
| if err is not None: |
| return err |
| |
| err = write_ulong(os.path.join(dir_path, 'nr_accesses', 'min'), |
| pattern.nr_acc_min_max[0].samples) |
| if err is not None: |
| return err |
| |
| err = write_ulong(os.path.join(dir_path, 'nr_accesses', 'max'), |
| pattern.nr_acc_min_max[1].samples) |
| if err is not None: |
| return err |
| |
| err = write_ulong(os.path.join(dir_path, 'age', 'min'), |
| pattern.age_min_max[0].aggr_intervals) |
| if err is not None: |
| return err |
| return write_ulong(os.path.join(dir_path, 'age', 'max'), |
| pattern.age_min_max[1].aggr_intervals) |
| |
| def write_scheme_dir(dir_path, scheme): |
| err = write_scheme_access_pattern_dir( |
| os.path.join(dir_path, 'access_pattern'), scheme.access_pattern) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file(os.path.join(dir_path, 'action'), scheme.action) |
| if err is not None: |
| return err |
| |
| err = write_scheme_dests_dir( |
| os.path.join(dir_path, 'dests'), scheme.dests) |
| if err is not None: |
| return err |
| |
| err = write_quotas_dir(os.path.join(dir_path, 'quotas'), scheme.quotas) |
| if err is not None: |
| return err |
| |
| err = write_watermarks_dir( |
| os.path.join(dir_path, 'watermarks'), scheme.watermarks) |
| if err is not None: |
| return err |
| |
| if scheme.target_nid is not None: |
| err = _damo_fs.write_file(os.path.join(dir_path,'target_nid'), '%s' % scheme.target_nid) |
| if err is not None: |
| return err |
| |
| err = write_filters_dirs(dir_path, scheme.filters) |
| if err is not None: |
| return err |
| |
| apply_interval_file = os.path.join(dir_path, 'apply_interval_us') |
| # schemes apply interval is merged in v6.7-rc1 |
| if os.path.isfile(apply_interval_file): |
| err = _damo_fs.write_file(apply_interval_file, |
| '%d' % scheme.apply_interval_us) |
| if err is not None: |
| return err |
| else: |
| if scheme.apply_interval_us: |
| return 'the kernel is not supporting schemes apply interval' |
| |
| max_nr_snapshots_file = os.path.join(dir_path, 'stats', 'max_nr_snapshots') |
| if os.path.isfile(max_nr_snapshots_file): |
| err = _damo_fs.write_file(max_nr_snapshots_file, |
| '%d' % scheme.stats.max_nr_snapshots) |
| if err is not None: |
| return err |
| else: |
| if scheme.stats.max_nr_snapshots: |
| return 'the kernel is not supporting max_nr_snapshots' |
| |
| return None |
| |
| def write_schemes_dir(dir_path, schemes): |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_schemes'), schemes) |
| if err is not None: |
| return err |
| |
| for idx, scheme in enumerate(schemes): |
| err = write_scheme_dir(os.path.join(dir_path, '%d' % idx), scheme) |
| if err is not None: |
| return err |
| |
| def write_target_region_dir(dir_path, region): |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'start'), '%d' % region.start) |
| if err is not None: |
| return err |
| |
| return _damo_fs.write_file( |
| os.path.join(dir_path, 'end'), '%d' % region.end) |
| |
| def write_target_regions_dir(dir_path, regions): |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_regions'), regions) |
| if err is not None: |
| return err |
| |
| for idx, region in enumerate(regions): |
| err = write_target_region_dir( |
| os.path.join(dir_path, '%d' % idx), region) |
| if err is not None: |
| return err |
| return None |
| |
| def write_target_dir(dir_path, target): |
| if target.pid is not None: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'pid_target'), '%s' % target.pid) |
| if err is not None: |
| return err |
| |
| obsolete_file = os.path.join(dir_path, 'obsolete_target') |
| if os.path.isfile(obsolete_file): |
| err = _damo_fs.write_file( |
| obsolete_file, '1' if target.obsolete else '0') |
| if err is not None: |
| return err |
| elif target.obsolete: |
| return 'obsolete_target unsupported' |
| |
| return write_target_regions_dir( |
| os.path.join(dir_path, 'regions'), target.regions) |
| |
| |
| def write_targets_dir(dir_path, targets): |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_targets'), targets) |
| if err is not None: |
| return err |
| |
| for idx, target in enumerate(targets): |
| err = write_target_dir(os.path.join(dir_path, '%d' % idx), target) |
| if err is not None: |
| return err |
| return None |
| |
| def write_monitoring_intervals_goal_dir(dir_path, goal): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'access_bp'), '%d' % |
| goal.access_bp) |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, 'aggrs'), '%d' % |
| goal.aggrs) |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, 'min_sample_us'), '%d' % |
| goal.min_sample_us) |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, 'max_sample_us'), '%d' % |
| goal.max_sample_us) |
| if err is not None: |
| return err |
| |
| def write_ops_attrs_dir(dir_path, ops_attrs): |
| if not os.path.isdir(dir_path): |
| return None |
| err = _damo_fs.write_file(os.path.join(dir_path, 'use_reports'), |
| 'Y' if ops_attrs.use_reports else 'N') |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, 'write_only'), |
| 'Y' if ops_attrs.write_only else 'N') |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, 'cpus'), ops_attrs.cpus) |
| if err is not None: |
| return err |
| err = _damo_fs.write_file(os.path.join(dir_path, 'tids'), ops_attrs.tids) |
| if err is not None: |
| return err |
| |
| def write_sample_filter_dir(dir_path, sample_filter): |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'type'), sample_filter.filter_type) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'matching'), |
| 'Y' if sample_filter.matching else 'N') |
| if err is not None: |
| return err |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'allow'), |
| 'Y' if sample_filter.allow else 'N') |
| if err is not None: |
| return err |
| |
| if sample_filter.filter_type == _damon.damon_filter_type_cpumask: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'cpumask'), sample_filter.cpumask) |
| if err is not None: |
| return err |
| elif sample_filter.filter_type == _damon.damon_filter_type_threads: |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'tid_arr'), sample_filter.tid_arr) |
| if err is not None: |
| return err |
| return None |
| |
| def write_sample_filters_dir(dir_path, filters): |
| if not os.path.isdir(dir_path): |
| if len(filters) == 0: |
| return None |
| return 'the kernel is not supporting sample filters' |
| |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_filters'), filters) |
| if err is not None: |
| return err |
| |
| for idx, sample_filter in enumerate(filters): |
| err = write_sample_filter_dir( |
| os.path.join(dir_path, '%d' % idx), sample_filter) |
| if err is not None: |
| return err |
| return None |
| |
| err = _damo_fs.write_file |
| |
| def write_sample_control_dir(dir_path, sample_control): |
| if not os.path.isdir(dir_path): |
| return None |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'primitives', 'page_table'), |
| 'Y' if sample_control.primitives_enabled.page_table else 'N') |
| if err is not None: |
| return err |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'primitives', 'page_fault'), |
| 'Y' if sample_control.primitives_enabled.page_fault else 'N') |
| if err is not None: |
| return err |
| return write_sample_filters_dir( |
| os.path.join(dir_path, 'filters'), sample_control.sample_filters) |
| |
| def write_monitoring_attrs_dir(dir_path, context): |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'intervals', 'sample_us'), |
| '%d' % context.intervals.sample) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'intervals', 'aggr_us'), |
| '%d' % context.intervals.aggr) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'intervals', 'update_us'), |
| '%d' % context.intervals.ops_update) |
| if err is not None: |
| return err |
| |
| intervals_goal_path = os.path.join(dir_path, 'intervals', 'intervals_goal') |
| if os.path.isdir(intervals_goal_path): |
| err = write_monitoring_intervals_goal_dir( |
| intervals_goal_path, context.intervals.intervals_goal) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'nr_regions', 'min'), |
| '%d' % context.nr_regions.minimum) |
| if err is not None: |
| return err |
| |
| err = _damo_fs.write_file( |
| os.path.join(dir_path, 'nr_regions', 'max'), |
| '%d' % context.nr_regions.maximum) |
| if err is not None: |
| return err |
| |
| return write_sample_control_dir( |
| os.path.join(dir_path, 'sample'), context.sample_control) |
| |
| def write_context_dir(dir_path, context): |
| err = _damo_fs.write_file(os.path.join(dir_path, 'operations'), |
| context.ops) |
| if err is not None: |
| return err |
| |
| err = write_ops_attrs_dir( |
| os.path.join(dir_path, 'operations_attrs'), context.ops_attrs) |
| if err is not None: |
| return err |
| |
| err = write_monitoring_attrs_dir( |
| os.path.join(dir_path, 'monitoring_attrs'), context) |
| if err is not None: |
| return err |
| |
| err = write_targets_dir( |
| os.path.join(dir_path, 'targets'), context.targets) |
| if err is not None: |
| return err |
| |
| for scheme in context.schemes: |
| scheme.access_pattern = scheme.access_pattern.converted_for_units( |
| _damon.unit_samples, _damon.unit_aggr_intervals, |
| context.intervals) |
| return write_schemes_dir( |
| os.path.join(dir_path, 'schemes'), context.schemes) |
| |
| def write_contexts_dir(dir_path, contexts): |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_contexts'), contexts) |
| if err is not None: |
| return err |
| |
| for idx, context in enumerate(contexts): |
| err = write_context_dir( |
| os.path.join(dir_path, '%d' % idx), context) |
| if err is not None: |
| return err |
| |
| def write_kdamond_dir(dir_path, kdamond): |
| refresh_ms_path = os.path.join(dir_path, 'refresh_ms') |
| if os.path.isfile(refresh_ms_path): |
| err = _damo_fs.write_file(refresh_ms_path, '%d' % kdamond.refresh_ms) |
| if err is not None: |
| return err |
| |
| err = write_contexts_dir( |
| os.path.join(dir_path, 'contexts'), kdamond.contexts) |
| if err is not None: |
| return err |
| |
| def write_kdamonds_dir(dir_path, kdamonds): |
| err = ensure_nr_file_for(os.path.join(dir_path, 'nr_kdamonds'), kdamonds) |
| if err: |
| return err |
| |
| for idx, kdamond in enumerate(kdamonds): |
| err = write_kdamond_dir(os.path.join(dir_path, '%d' % idx), kdamond) |
| if err is not None: |
| return err |
| |
| def stage_kdamonds(kdamonds): |
| """Write DAMON parameters for kdamonds to the sysfs files. |
| |
| Args: |
| kdamonds: A list of _damon.Kdamond objects. |
| |
| Returns: |
| None for success, an error string if failed. |
| """ |
| # Assume caller checked supported() |
| return write_kdamonds_dir(get_kdamonds_dir(), kdamonds) |
| |
| def stage_kdamonds_targets(kdamonds): |
| kdamonds_dir_path = get_kdamonds_dir() |
| |
| err = ensure_nr_file_for(os.path.join(kdamonds_dir_path, 'nr_kdamonds'), kdamonds) |
| if err: |
| return err |
| |
| for k_idx, kdamond in enumerate(kdamonds): |
| contexts_dir_path = os.path.join(kdamonds_dir_path, '%d' % k_idx, 'contexts') |
| contexts = kdamond.contexts |
| |
| err = ensure_nr_file_for(os.path.join(contexts_dir_path, 'nr_contexts'), contexts) |
| if err: |
| return err |
| |
| for c_idx, context in enumerate(contexts): |
| targets_dir_path = os.path.join(contexts_dir_path, '%d' % c_idx, 'targets') |
| err = write_targets_dir(targets_dir_path, context.targets) |
| if err: |
| return err |
| |
| return None |
| |
| # for current_kdamonds() |
| |
| def numbered_dirs_content(files_content, nr_filename): |
| nr_dirs = int(files_content[nr_filename]) |
| number_dirs = [] |
| for i in range(nr_dirs): |
| number_dirs.append(files_content['%d' % i]) |
| return number_dirs |
| |
| def number_sorted_dirs(files_content): |
| number_dirs = {} |
| for filename, content in files_content.items(): |
| try: |
| nr = int(filename) |
| except: |
| continue |
| if type(content) != dict: |
| continue |
| number_dirs[nr] = content |
| sorted_numbers = sorted(number_dirs.keys()) |
| return [number_dirs[nr] for nr in sorted_numbers] |
| |
| def files_content_to_access_pattern(files_content): |
| return _damon.DamosAccessPattern( |
| [int(files_content['sz']['min']), |
| int(files_content['sz']['max'])], |
| [int(files_content['nr_accesses']['min']), |
| int(files_content['nr_accesses']['max'])], |
| _damon.unit_samples, # nr_accesses_unit |
| [int(files_content['age']['min']), |
| int(files_content['age']['max'])], |
| _damon.unit_aggr_intervals) # age_unit |
| |
| def files_content_to_quota_goals(files_content): |
| goals = [] |
| for goal_kv in number_sorted_dirs(files_content): |
| if 'target_metric' in goal_kv: |
| if 'path' in goal_kv: |
| memcg_path = goal_kv['path'].strip() |
| else: |
| memcg_path = None |
| goals.append( |
| _damon.DamosQuotaGoal( |
| metric=goal_kv['target_metric'].strip(), |
| nid=goal_kv['nid'] if 'nid' in goal_kv else None, |
| memcg_path=memcg_path, |
| target_value=goal_kv['target_value'], |
| current_value=goal_kv['current_value'])) |
| else: |
| goals.append( |
| _damon.DamosQuotaGoal( |
| target_value=goal_kv['target_value'], |
| current_value=goal_kv['current_value'])) |
| return goals |
| |
| def files_content_to_quotas(files_content): |
| return _damon.DamosQuotas( |
| int(files_content['ms']), |
| int(files_content['bytes']), |
| int(files_content['reset_interval_ms']), |
| [int(files_content['weights']['sz_permil']), |
| int(files_content['weights']['nr_accesses_permil']), |
| int(files_content['weights']['age_permil'])], |
| files_content_to_quota_goals(files_content['goals']) |
| if 'goals' in files_content else [], |
| int(files_content['effective_bytes']) |
| if 'effective_bytes' in files_content else 0) |
| |
| def files_content_to_watermarks(files_content): |
| return _damon.DamosWatermarks( |
| files_content['metric'].strip(), |
| int(files_content['interval_us']), |
| int(files_content['high']), |
| int(files_content['mid']), |
| int(files_content['low'])) |
| |
| def files_content_to_damos_filter(files_content): |
| allow = False |
| if 'allow' in files_content: |
| allow = files_content['allow'].strip() |
| # the file name has changed from pass to allow after v1 patch |
| elif 'pass' in files_content: |
| allow = files_content['pass'].strip() |
| |
| hugepage_size=None |
| if 'min' in files_content and 'max' in files_content: |
| hugepage_size = [_damo_fmt_str.text_to_bytes(x) |
| for x in [files_content['min'], files_content['max']]] |
| |
| return _damon.DamosFilter( |
| files_content['type'].strip(), |
| files_content['matching'].strip(), |
| allow, |
| files_content['memcg_path'].strip(), |
| _damon.DamonRegion(files_content['addr_start'].strip(), |
| files_content['addr_end'].strip()) |
| if 'addr_start' in files_content and 'addr_end' in files_content |
| else None, |
| files_content['damon_target_idx'] |
| if 'damon_target_idx' in files_content else None, |
| hugepage_size=hugepage_size) |
| |
| def files_content_to_damos_filters(scheme_files_content): |
| filters = [] |
| if 'core_filters' in scheme_files_content: |
| filters += [files_content_to_damos_filter(filter_kv) |
| for filter_kv in numbered_dirs_content( |
| scheme_files_content['core_filters'], 'nr_filters')] |
| filters += [files_content_to_damos_filter(filter_kv) |
| for filter_kv in numbered_dirs_content( |
| scheme_files_content['ops_filters'], 'nr_filters')] |
| filters += [files_content_to_damos_filter(filter_kv) |
| for filter_kv in numbered_dirs_content( |
| scheme_files_content['filters'], 'nr_filters')] |
| return filters |
| |
| def files_content_to_damos_stats(files_content): |
| if 'nr_snapshots' in files_content: |
| nr_snapshots = int(files_content['nr_snapshots']) |
| else: |
| nr_snapshots = 0 |
| if 'max_nr_snapshots' in files_content: |
| max_nr_snapshots = int(files_content['max_nr_snapshots']) |
| else: |
| max_nr_snapshots = 0 |
| return _damon.DamosStats( |
| int(files_content['nr_tried']), |
| int(files_content['sz_tried']), |
| int(files_content['nr_applied']), |
| int(files_content['sz_applied']), |
| int(files_content['sz_ops_filter_passed'] |
| if 'sz_ops_filter_passed' in files_content else 0), |
| int(files_content['qt_exceeds']), |
| nr_snapshots=nr_snapshots, max_nr_snapshots=max_nr_snapshots) |
| |
| def files_content_to_damos_tried_regions(files_content): |
| return [_damon.DamonRegion( |
| int(kv['start']), int(kv['end']), |
| int(kv['nr_accesses']), _damon.unit_samples, |
| int(kv['age']), _damon.unit_aggr_intervals, |
| int(kv['sz_filter_passed']) |
| if 'sz_filter_passed' in kv else None, |
| ) for kv in number_sorted_dirs(files_content)] |
| |
| def files_content_to_damos_dest(files_content): |
| return _damon.DamosDest( |
| id=int(files_content['id']), weight=int(files_content['weight'])) |
| |
| def files_content_to_scheme(files_content): |
| dests = [] |
| if 'dests' in files_content: |
| dests = [files_content_to_damos_dest(content) |
| for content in numbered_dirs_content( |
| files_content['dests'], 'nr_dests')] |
| return _damon.Damos( |
| files_content_to_access_pattern(files_content['access_pattern']), |
| files_content['action'].strip(), |
| files_content['target_nid'].strip() |
| if 'target_nid' in files_content else None, |
| files_content['apply_interval_us'].strip() |
| if 'apply_interval_us' in files_content else None, |
| files_content_to_quotas(files_content['quotas']), |
| files_content_to_watermarks(files_content['watermarks']), |
| files_content_to_damos_filters(files_content) |
| if 'filters' in files_content else [], |
| files_content_to_damos_stats(files_content['stats']), |
| files_content_to_damos_tried_regions( |
| files_content['tried_regions']) |
| if 'tried_regions' in files_content else [], |
| files_content['tried_regions']['total_bytes'] |
| if 'tried_regions' in files_content and |
| 'total_bytes' in files_content['tried_regions'] else None, |
| dests=dests) |
| |
| def files_content_to_regions(files_content): |
| return [_damon.DamonRegion( |
| int(kv['start']), int(kv['end'])) |
| for kv in numbered_dirs_content(files_content, 'nr_regions')] |
| |
| def files_content_to_target(files_content): |
| try: |
| pid = int(files_content['pid_target']) |
| except: |
| pid = None |
| obsolete = False |
| if 'obsolete_target' in files_content: |
| obsolete = files_content['obsolete_target'].strip() |
| regions = files_content_to_regions(files_content['regions']) |
| return _damon.DamonTarget(pid, regions, obsolete=obsolete) |
| |
| def files_content_to_sample_filter(files_content): |
| filter_type = files_content['type'].strip() |
| matching = files_content['matching'].strip() |
| allow = files_content['matching'].strip() |
| cpumask = files_content['cpumask'].strip() |
| tid_arr = files_content['tid_arr'].strip() |
| return _damon.DamonSampleFilter( |
| filter_type=filter_type, matching=matching, allow=allow, |
| cpumask=cpumask, tid_arr=tid_arr) |
| |
| def files_content_to_sample_filters(files_content): |
| return [files_content_to_sample_filter(filter_kv) |
| for filter_kv in numbered_dirs_content( |
| files_content, 'nr_filters')] |
| |
| def files_content_to_sample_control(files_content): |
| page_table = files_content['primitives']['page_table'].strip() |
| page_fault = files_content['primitives']['page_fault'].strip() |
| primitives_enabled = _damon.DamonPrimitivesEnabled( |
| page_table=page_table, page_fault=page_fault) |
| sample_filters = files_content_to_sample_filters(files_content['filters']) |
| return _damon.DamonSampleControl( |
| primitives_enabled=primitives_enabled, |
| sample_filters=sample_filters) |
| |
| def files_content_to_ops_attrs(files_content): |
| use_reports = files_content['use_reports'].strip() |
| write_only = files_content['write_only'].strip() |
| cpus = files_content['cpus'].strip() |
| tids = files_content['tids'].strip() |
| return _damon.OpsAttrs(use_reports=use_reports, write_only=write_only, |
| cpus=cpus, tids=tids) |
| |
| def files_content_to_context(files_content): |
| mon_attrs_content = files_content['monitoring_attrs'] |
| intervals_content = mon_attrs_content['intervals'] |
| if 'intervals_goal' in intervals_content: |
| kvpairs = intervals_content['intervals_goal'] |
| intervals_goal = _damon.DamonIntervalsGoal( |
| int(kvpairs['access_bp']), int(kvpairs['aggrs']), |
| int(kvpairs['min_sample_us']), int(kvpairs['max_sample_us'])) |
| else: |
| intervals_goal = _damon.DamonIntervalsGoal() |
| intervals = _damon.DamonIntervals( |
| int(intervals_content['sample_us']), |
| int(intervals_content['aggr_us']), |
| int(intervals_content['update_us']), |
| intervals_goal) |
| nr_regions_content = mon_attrs_content['nr_regions'] |
| nr_regions = _damon.DamonNrRegionsRange( |
| int(nr_regions_content['min']), |
| int(nr_regions_content['max'])) |
| if 'sample' in mon_attrs_content: |
| sample_control = files_content_to_sample_control( |
| mon_attrs_content['sample']) |
| else: |
| sample_control = _damon.DamonSampleControl() |
| ops = files_content['operations'].strip() |
| if 'operations_attrs' in files_content: |
| ops_attrs = files_content_to_ops_attrs( |
| files_content['operations_attrs']) |
| else: |
| ops_attrs = _damon.OpsAttrs() |
| |
| targets_content = files_content['targets'] |
| targets = [files_content_to_target(content) |
| for content in numbered_dirs_content( |
| targets_content, 'nr_targets')] |
| |
| schemes_content = files_content['schemes'] |
| schemes = [files_content_to_scheme(content) |
| for content in numbered_dirs_content( |
| schemes_content, 'nr_schemes')] |
| |
| return _damon.DamonCtx(ops, targets, intervals, nr_regions, schemes, |
| ops_attrs=ops_attrs, sample_control=sample_control) |
| |
| def files_content_to_kdamond(files_content): |
| contexts_content = files_content['contexts'] |
| contexts = [files_content_to_context(content) |
| for content in numbered_dirs_content( |
| contexts_content, 'nr_contexts')] |
| state = files_content['state'].strip() |
| pid = files_content['pid'].strip() |
| if 'refresh_ms' in files_content: |
| refresh_ms = files_content['refresh_ms'].strip() |
| else: |
| refresh_ms = 0 |
| return _damon.Kdamond(state, pid, refresh_ms=refresh_ms, contexts=contexts) |
| |
| def files_content_to_kdamonds(files_contents): |
| return [files_content_to_kdamond(content) |
| for content in numbered_dirs_content( |
| files_contents, 'nr_kdamonds')] |
| |
| def current_kdamonds(): |
| # Assume caller checked supported() |
| return files_content_to_kdamonds(_damo_fs.read_files(get_kdamonds_dir())) |
| |
| def get_nr_kdamonds_file(): |
| return os.path.join(get_kdamonds_dir(), 'nr_kdamonds') |
| |
| def nr_kdamonds(): |
| nr_kdamonds, err = _damo_fs.read_file(get_nr_kdamonds_file()) |
| if err != None: |
| raise Exception('nr_kdamonds_file read fail (%s)' % err) |
| return int(nr_kdamonds) |
| |
| def commit_staged(kdamond_idxs): |
| for kdamond_idx in kdamond_idxs: |
| err = _damo_fs.write_file(get_state_file_of(kdamond_idx), 'commit') |
| if err != None: |
| return err |
| return None |
| |
| def commit_quota_goals(kdamond_idxs): |
| for kdamond_idx in kdamond_idxs: |
| err = _damo_fs.write_file(get_state_file_of(kdamond_idx), |
| 'commit_schemes_quota_goals') |
| if err != None: |
| return err |
| |
| # features |
| |
| # sysfs was merged in v5.18-rc1 |
| features_sysfs_support_from_begining = [ |
| 'sysfs/schemes', |
| 'sysfs/init_regions', |
| 'sysfs/vaddr', |
| 'sysfs/paddr', |
| 'sysfs/schemes_size_quota', |
| 'sysfs/schemes_time_quota', |
| 'sysfs/schemes_prioritization', |
| 'sysfs/schemes_wmarks', |
| 'sysfs/schemes_stat_succ', |
| 'sysfs/schemes_stat_qt_exceed', |
| ] |
| |
| def kdamond_dir_of(kdamond_idx): |
| return os.path.join(get_kdamonds_dir(), '%s' % kdamond_idx) |
| |
| def ctx_dir_of(kdamond_idx, context_idx): |
| return os.path.join( |
| kdamond_dir_of(kdamond_idx), 'contexts', '%s' % context_idx) |
| |
| def target_dir_of(kdamond_idx, context_idx, target_idx): |
| return os.path.join( |
| ctx_dir_of(kdamond_idx, context_idx), 'targets', |
| '%s' % target_idx) |
| |
| def schemes_dir_of(kdamond_idx, context_idx): |
| return os.path.join(ctx_dir_of(kdamond_idx, context_idx), 'schemes') |
| |
| def _avail_ops(): |
| '''Assumes called by update_supported_features() assuming one scheme. |
| Returns available ops input and error''' |
| avail_ops = [] |
| avail_operations_filepath = os.path.join(ctx_dir_of(0, 0), |
| 'avail_operations') |
| if not os.path.isfile(avail_operations_filepath): |
| operations_filepath = os.path.join(ctx_dir_of(0, 0), 'operations') |
| for ops in ['vaddr', 'paddr', 'fvaddr']: |
| err = _damo_fs.write_file(operations_filepath, ops) |
| if err != None: |
| avail_ops.append(ops) |
| return avail_ops, None |
| |
| content, err = _damo_fs.read_file(avail_operations_filepath) |
| if err != None: |
| return None, err |
| return content.strip().split(), None |
| |
| def scheme_dir_of(kdamond_idx, context_idx, scheme_idx): |
| return os.path.join( |
| schemes_dir_of(kdamond_idx, context_idx), '%s' % scheme_idx) |
| |
| def scheme_tried_regions_dir_of(kdamond_idx, context_idx, scheme_idx): |
| return os.path.join( |
| scheme_dir_of(kdamond_idx, context_idx, scheme_idx), |
| 'tried_regions') |
| |
| def mk_feature_supports_map(): |
| ''' |
| Returns a map indicating list of supported and unsupported DAMON features, |
| and an error if making the map failed. |
| Keys of the map are names of DAMON features. |
| Values are bool indicating whether the feature is supported. |
| ''' |
| supports_map = {x.name: False for x in _damon_features.features_list} |
| |
| for feature in features_sysfs_support_from_begining: |
| supports_map[feature] = True |
| |
| orig_kdamonds = current_kdamonds() |
| # While DAMON is running, feature checking I/O can fail, corrupt something, |
| # or make something complicated. Just don't do that. |
| for kd in orig_kdamonds: |
| if kd.state == 'on': |
| return None, 'DAMON is running' |
| |
| kdamonds_for_feature_check = [ |
| _damon.Kdamond( |
| state=None, pid=None, contexts=[ |
| _damon.DamonCtx( |
| targets=[_damon.DamonTarget( |
| pid=None, regions=[])], |
| schemes=[_damon.Damos()])])] |
| err = stage_kdamonds(kdamonds_for_feature_check) |
| if err is not None: |
| stage_kdamonds(orig_kdamonds) |
| return None, 'staging feature check purpose kdamond failed' |
| |
| if os.path.isdir(scheme_tried_regions_dir_of(0, 0, 0)): |
| supports_map['sysfs/schemes_tried_regions'] = True |
| |
| if os.path.isfile(os.path.join(scheme_tried_regions_dir_of(0, 0, 0), |
| 'total_bytes')): |
| supports_map['sysfs/schemes_tried_regions_sz'] = True |
| # address and target filter types are added in v6.6-rc1, together with |
| # schemes_tried_regions_sz |
| supports_map['sysfs/schemes_filters_addr'] = True |
| supports_map['sysfs/schemes_filters_target'] = True |
| |
| if os.path.isdir(os.path.join(scheme_dir_of(0, 0, 0), 'filters')): |
| supports_map['sysfs/schemes_filters'] = True |
| # anon and memcg were supported from the beginning |
| supports_map['sysfs/schemes_filters_anon'] = True |
| supports_map['sysfs/schemes_filters_memcg'] = True |
| kdamonds_for_feature_check[0].contexts[0].schemes[0].filters = [ |
| _damon.DamosFilter('young', True)] |
| err = stage_kdamonds(kdamonds_for_feature_check) |
| if err is None: |
| supports_map['sysfs/schemes_filters_young'] = True |
| |
| if os.path.isfile(os.path.join(scheme_dir_of(0, 0, 0), 'apply_interval_us')): |
| supports_map['sysfs/schemes_apply_interval'] = True |
| |
| if os.path.isdir(os.path.join(scheme_dir_of(0, 0, 0), 'quotas', 'goals')): |
| supports_map['sysfs/schemes_quota_goals'] = True |
| |
| if os.path.isfile(os.path.join(scheme_dir_of(0, 0, 0), 'quotas', |
| 'effective_bytes')): |
| supports_map['sysfs/schemes_quota_effective_bytes'] = True |
| # goal_metric and goal_some_psi will be merged together with effective bytes. |
| supports_map['sysfs/schemes_quota_goal_metric'] = True |
| supports_map['sysfs/schemes_quota_goal_some_psi'] = True |
| |
| if os.path.isfile(os.path.join(scheme_dir_of(0, 0, 0), 'target_nid')): |
| supports_map['sysfs/schemes_migrate'] = True |
| |
| if os.path.isfile( |
| os.path.join(scheme_dir_of(0, 0, 0), |
| 'stats', 'sz_ops_filter_passed')): |
| supports_map['sysfs/sz_ops_filter_passed'] = True |
| |
| ops_filters_dir = os.path.join(scheme_dir_of(0, 0, 0), 'ops_filters') |
| if not os.path.isdir(ops_filters_dir): |
| ops_filters_dir = os.path.join(scheme_dir_of(0, 0, 0), 'filters') |
| |
| if os.path.isfile( |
| os.path.join(ops_filters_dir, '0', 'allow')): |
| supports_map['sysfs/allow_filter'] = True |
| |
| if os.path.isfile( |
| os.path.join(ops_filters_dir, '0', 'min')): |
| supports_map['sysfs/schemes_filters_hugepage_size'] = True |
| |
| if os.path.isdir( |
| os.path.join(ctx_dir_of(0, 0), |
| 'monitoring_attrs', 'intervals', 'intervals_goal')): |
| supports_map['sysfs/intervals_goal'] = True |
| |
| if os.path.isdir( |
| os.path.join(scheme_dir_of(0, 0, 0), 'core_filters')): |
| supports_map['sysfs/schemes_filters_core_ops_dirs'] = True |
| |
| # unmapped and active pages DAMOS filters are merged into v6.15 |
| # together with core_ops_dirs |
| supports_map['sysfs/schemes_filters_unmapped'] = True |
| supports_map['sysfs/schemes_filters_active'] = True |
| |
| if supports_map['sysfs/schemes_quota_goals'] is True: |
| kdamonds_for_feature_check = [ |
| _damon.Kdamond( |
| state=None, pid=None, contexts=[ |
| _damon.DamonCtx( |
| targets=[_damon.DamonTarget( |
| pid=None, regions=[])], |
| schemes=[_damon.Damos( |
| quotas=_damon.DamosQuotas( |
| goals=[_damon.DamosQuotaGoal()]) |
| )])])] |
| err = stage_kdamonds(kdamonds_for_feature_check) |
| if err is not None: |
| stage_kdamonds(orig_kdamonds) |
| return None, \ |
| 'staging damos goal feature check purpose kdamond failed' |
| |
| if os.path.isfile( |
| os.path.join(scheme_dir_of(0, 0, 0), 'quotas', 'goals', '0', |
| 'nid')): |
| supports_map['sysfs/schemes_quota_goal_node_mem_used_free'] = True |
| |
| if os.path.isfile( |
| os.path.join(scheme_dir_of(0, 0, 0), 'quotas', 'goals', '0', |
| 'path')): |
| supports_map['sysfs/schemes_quota_goal_node_memcg_used_free'] = True |
| |
| if os.path.isdir(os.path.join(scheme_dir_of(0, 0, 0), 'dests')): |
| supports_map['sysfs/schemes_dests'] = True |
| |
| if os.path.isfile(os.path.join(kdamond_dir_of(0), 'refresh_ms')): |
| supports_map['sysfs/refresh_ms'] = True |
| |
| if os.path.isfile(os.path.join(ctx_dir_of(0, 0), 'addr_unit')): |
| supports_map['sysfs/addr_unit'] = True |
| |
| if os.path.isfile(os.path.join(target_dir_of(0, 0, 0), 'obsolete_target')): |
| supports_map['sysfs/obsolete_target'] = True |
| |
| if os.path.isfile( |
| os.path.join(scheme_dir_of(0, 0, 0), |
| 'stats', 'nr_snapshots')): |
| supports_map['syfs/damos_stat_nr_snapshots'] = True |
| |
| if os.path.isfile( |
| os.path.join(scheme_dir_of(0, 0, 0), |
| 'stats', 'max_nr_snapshots')): |
| supports_map['sysfs/damos_max_nr_snapshots'] = True |
| |
| if os.path.isdir( |
| os.path.join(ctx_dir_of(0, 0), |
| 'monitoring_attrs', 'sample')): |
| supports_map['sysfs/damon_sample_control'] = True |
| |
| if os.path.isdir(os.path.join(ctx_dir_of(0, 0), 'operations_attrs')): |
| supports_map['sysfs/ops_attrs'] = True |
| |
| avail_ops, err = _avail_ops() |
| if err == None: |
| for ops in ['vaddr', 'paddr', 'fvaddr']: |
| supports_map['sysfs/%s' % ops] = ops in avail_ops |
| err = stage_kdamonds(orig_kdamonds) |
| if err is not None: |
| return None, 'restoring original kdamonds setup failed' |
| return supports_map, None |