# SPDX-License-Identifier: GPL-2.0 import os sysfs_root = '/sys/kernel/mm/damon/admin' def write_file(path, string): "Returns error string if failed, or None otherwise" string = '%s' % string try: with open(path, 'w') as f: f.write(string) except Exception as e: return '%s' % e return None def read_file(path): '''Returns the read content and error string. The read content is None if the reading failed''' try: with open(path, 'r') as f: return f.read(), None except Exception as e: return None, '%s' % e class DamosAccessPattern: size = None nr_accesses = None age = None scheme = None def __init__(self, size=None, nr_accesses=None, age=None): self.size = size self.nr_accesses = nr_accesses self.age = age if self.size == None: self.size = [0, 2**64 - 1] if self.nr_accesses == None: self.nr_accesses = [0, 2**64 - 1] if self.age == None: self.age = [0, 2**64 - 1] def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'access_pattern') def stage(self): err = write_file( os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0]) if err != None: return err err = write_file( os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1]) if err != None: return err err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'), self.nr_accesses[0]) if err != None: return err err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'), self.nr_accesses[1]) if err != None: return err err = write_file( os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0]) if err != None: return err err = write_file( os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1]) if err != None: return err class DamosQuota: sz = None # size quota, in bytes ms = None # time quota reset_interval_ms = None # quota reset interval scheme = None # owner scheme def __init__(self, sz=0, ms=0, reset_interval_ms=0): self.sz = sz self.ms = ms self.reset_interval_ms = reset_interval_ms def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'quotas') def stage(self): err = write_file(os.path.join(self.sysfs_dir(), 'bytes'), self.sz) if err != None: return err err = write_file(os.path.join(self.sysfs_dir(), 'ms'), self.ms) if err != None: return err err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms'), self.reset_interval_ms) if err != None: return err class DamosStats: nr_tried = None sz_tried = None nr_applied = None sz_applied = None qt_exceeds = None def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds): self.nr_tried = nr_tried self.sz_tried = sz_tried self.nr_applied = nr_applied self.sz_applied = sz_applied self.qt_exceeds = qt_exceeds class Damos: action = None access_pattern = None quota = None apply_interval_us = None # todo: Support watermarks, stats, tried_regions idx = None context = None tried_bytes = None stats = None def __init__(self, action='stat', access_pattern=DamosAccessPattern(), quota=DamosQuota(), apply_interval_us=0): self.action = action self.access_pattern = access_pattern self.access_pattern.scheme = self self.quota = quota self.quota.scheme = self self.apply_interval_us = apply_interval_us def sysfs_dir(self): return os.path.join( self.context.sysfs_dir(), 'schemes', '%d' % self.idx) def stage(self): err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action) if err != None: return err err = self.access_pattern.stage() if err != None: return err err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'), '%d' % self.apply_interval_us) if err != None: return err err = self.quota.stage() if err != None: return err # disable watermarks err = write_file( os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none') if err != None: return err # disable filters err = write_file( os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0') if err != None: return err class DamonTarget: pid = None # todo: Support target regions if test is made idx = None context = None def __init__(self, pid): self.pid = pid def sysfs_dir(self): return os.path.join( self.context.sysfs_dir(), 'targets', '%d' % self.idx) def stage(self): err = write_file( os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0') if err != None: return err return write_file( os.path.join(self.sysfs_dir(), 'pid_target'), self.pid) class DamonAttrs: sample_us = None aggr_us = None update_us = None min_nr_regions = None max_nr_regions = None context = None def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000, min_nr_regions=10, max_nr_regions=1000): self.sample_us = sample_us self.aggr_us = aggr_us self.update_us = update_us self.min_nr_regions = min_nr_regions self.max_nr_regions = max_nr_regions def interval_sysfs_dir(self): return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs', 'intervals') def nr_regions_range_sysfs_dir(self): return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs', 'nr_regions') def stage(self): err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'), self.sample_us) if err != None: return err err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'), self.aggr_us) if err != None: return err err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'), self.update_us) if err != None: return err err = write_file( os.path.join(self.nr_regions_range_sysfs_dir(), 'min'), self.min_nr_regions) if err != None: return err err = write_file( os.path.join(self.nr_regions_range_sysfs_dir(), 'max'), self.max_nr_regions) if err != None: return err class DamonCtx: ops = None monitoring_attrs = None targets = None schemes = None kdamond = None idx = None def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[], schemes=[]): self.ops = ops self.monitoring_attrs = monitoring_attrs self.monitoring_attrs.context = self self.targets = targets for idx, target in enumerate(self.targets): target.idx = idx target.context = self self.schemes = schemes for idx, scheme in enumerate(self.schemes): scheme.idx = idx scheme.context = self def sysfs_dir(self): return os.path.join(self.kdamond.sysfs_dir(), 'contexts', '%d' % self.idx) def stage(self): err = write_file( os.path.join(self.sysfs_dir(), 'operations'), self.ops) if err != None: return err err = self.monitoring_attrs.stage() if err != None: return err nr_targets_file = os.path.join( self.sysfs_dir(), 'targets', 'nr_targets') content, err = read_file(nr_targets_file) if err != None: return err if int(content) != len(self.targets): err = write_file(nr_targets_file, '%d' % len(self.targets)) if err != None: return err for target in self.targets: err = target.stage() if err != None: return err nr_schemes_file = os.path.join( self.sysfs_dir(), 'schemes', 'nr_schemes') content, err = read_file(nr_schemes_file) if int(content) != len(self.schemes): err = write_file(nr_schemes_file, '%d' % len(self.schemes)) if err != None: return err for scheme in self.schemes: err = scheme.stage() if err != None: return err return None class Kdamond: state = None pid = None contexts = None idx = None # index of this kdamond between siblings kdamonds = None # parent def __init__(self, contexts=[]): self.contexts = contexts for idx, context in enumerate(self.contexts): context.idx = idx context.kdamond = self def sysfs_dir(self): return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx) def start(self): nr_contexts_file = os.path.join(self.sysfs_dir(), 'contexts', 'nr_contexts') content, err = read_file(nr_contexts_file) if err != None: return err if int(content) != len(self.contexts): err = write_file(nr_contexts_file, '%d' % len(self.contexts)) if err != None: return err for context in self.contexts: err = context.stage() if err != None: return err err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on') return err def update_schemes_tried_bytes(self): err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'update_schemes_tried_bytes') if err != None: return err for context in self.contexts: for scheme in context.schemes: content, err = read_file(os.path.join(scheme.sysfs_dir(), 'tried_regions', 'total_bytes')) if err != None: return err scheme.tried_bytes = int(content) def update_schemes_stats(self): err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'update_schemes_stats') if err != None: return err for context in self.contexts: for scheme in context.schemes: stat_values = [] for stat in ['nr_tried', 'sz_tried', 'nr_applied', 'sz_applied', 'qt_exceeds']: content, err = read_file( os.path.join(scheme.sysfs_dir(), 'stats', stat)) if err != None: return err stat_values.append(int(content)) scheme.stats = DamosStats(*stat_values) class Kdamonds: kdamonds = [] def __init__(self, kdamonds=[]): self.kdamonds = kdamonds for idx, kdamond in enumerate(self.kdamonds): kdamond.idx = idx kdamond.kdamonds = self def sysfs_dir(self): return os.path.join(sysfs_root, 'kdamonds') def start(self): err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'), '%s' % len(self.kdamonds)) if err != None: return err for kdamond in self.kdamonds: err = kdamond.start() if err != None: return err return None