# SPDX-License-Identifier: GPL-2.0
import os
ksft_skip=4
sysfs_root = None
with open('/proc/mounts' , 'r' ) as f:
for line in f:
dev_name, mount_point, dev_fs = line.split()[:3]
if dev_fs == 'sysfs' :
sysfs_root = '%s/kernel/mm/damon/admin' % mount_point
break
if sysfs_root is None :
print('Seems sysfs not mounted?' )
exit(ksft_skip)
if not os.path.exists(sysfs_root):
print('Seems DAMON disabled?' )
exit(ksft_skip)
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 is None :
self.size = [0, 2**64 - 1]
if self.nr_accesses is None :
self.nr_accesses = [0, 2**32 - 1]
if self.age is None :
self.age = [0, 2**32 - 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 is not None :
return err
err = write_file(
os.path.join(self.sysfs_dir(), 'sz' , 'max' ), self.size[1])
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses' , 'min' ),
self.nr_accesses[0])
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses' , 'max' ),
self.nr_accesses[1])
if err is not None :
return err
err = write_file(
os.path.join(self.sysfs_dir(), 'age' , 'min' ), self.age[0])
if err is not None :
return err
err = write_file(
os.path.join(self.sysfs_dir(), 'age' , 'max' ), self.age[1])
if err is not None :
return err
qgoal_metric_user_input = 'user_input'
qgoal_metric_some_mem_psi_us = 'some_mem_psi_us'
qgoal_metrics = [qgoal_metric_user_input, qgoal_metric_some_mem_psi_us]
class DamosQuotaGoal:
metric = None
target_value = None
current_value = None
nid = None
effective_bytes = None
quota = None # owner quota
idx = None
def __init__(self, metric, target_value=10000, current_value=0, nid=0):
self.metric = metric
self.target_value = target_value
self.current_value = current_value
self.nid = nid
def sysfs_dir(self):
return os.path.join(self.quota.sysfs_dir(), 'goals' , '%d' % self.idx)
def stage(self):
err = write_file(os.path.join(self.sysfs_dir(), 'target_metric' ),
self.metric)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'target_value' ),
self.target_value)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'current_value' ),
self.current_value)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'nid' ), self.nid)
if err is not None :
return err
return None
class DamosQuota:
sz = None # size quota, in bytes
ms = None # time quota
goals = None # quota goals
reset_interval_ms = None # quota reset interval
weight_sz_permil = None
weight_nr_accesses_permil = None
weight_age_permil = None
scheme = None # owner scheme
def __init__(self, sz=0, ms=0, goals=None , reset_interval_ms=0,
weight_sz_permil=0, weight_nr_accesses_permil=0,
weight_age_permil=0):
self.sz = sz
self.ms = ms
self.reset_interval_ms = reset_interval_ms
self.weight_sz_permil = weight_sz_permil
self.weight_nr_accesses_permil = weight_nr_accesses_permil
self.weight_age_permil = weight_age_permil
self.goals = goals if goals is not None else []
for idx, goal in enumerate(self.goals):
goal.idx = idx
goal.quota = self
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 is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'ms' ), self.ms)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms' ),
self.reset_interval_ms)
if err is not None :
return err
err = write_file(os.path.join(
self.sysfs_dir(), 'weights' , 'sz_permil' ), self.weight_sz_permil)
if err is not None :
return err
err = write_file(os.path.join(
self.sysfs_dir(), 'weights' , 'nr_accesses_permil' ),
self.weight_nr_accesses_permil)
if err is not None :
return err
err = write_file(os.path.join(
self.sysfs_dir(), 'weights' , 'age_permil' ), self.weight_age_permil)
if err is not None :
return err
nr_goals_file = os.path.join(self.sysfs_dir(), 'goals' , 'nr_goals' )
content, err = read_file(nr_goals_file)
if err is not None :
return err
if int(content) != len(self.goals):
err = write_file(nr_goals_file, len(self.goals))
if err is not None :
return err
for goal in self.goals:
err = goal.stage()
if err is not None :
return err
return None
class DamosWatermarks:
metric = None
interval = None
high = None
mid = None
low = None
scheme = None # owner scheme
def __init__(self, metric='none' , interval=0, high=0, mid=0, low=0):
self.metric = metric
self.interval = interval
self.high = high
self.mid = mid
self.low = low
def sysfs_dir(self):
return os.path.join(self.scheme.sysfs_dir(), 'watermarks' )
def stage(self):
err = write_file(os.path.join(self.sysfs_dir(), 'metric' ), self.metric)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'interval_us' ),
self.interval)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'high' ), self.high)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'mid' ), self.mid)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'low' ), self.low)
if err is not None :
return err
class DamosFilter:
type_ = None
matching = None
allow = None
memcg_path = None
addr_start = None
addr_end = None
target_idx = None
min_ = None
max_ = None
idx = None
filters = None # owner filters
def __init__(self, type_='anon' , matching=False , allow=False ,
memcg_path='' , addr_start=0, addr_end=0, target_idx=0, min_=0,
max_=0):
self.type_ = type_
self.matching = matching
self.allow = allow
self.memcg_path = memcg_path,
self.addr_start = addr_start
self.addr_end = addr_end
self.target_idx = target_idx
self.min_ = min_
self.max_ = max_
def sysfs_dir(self):
return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx)
def stage(self):
err = write_file(os.path.join(self.sysfs_dir(), 'type' ), self.type_)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'matching' ),
self.matching)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'allow' ), self.allow)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'memcg_path' ),
self.memcg_path)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'addr_start' ),
self.addr_start)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'addr_end' ),
self.addr_end)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'damon_target_idx' ),
self.target_idx)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'min' ), self.min_)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'max' ), self.max_)
if err is not None :
return err
return None
class DamosFilters:
name = None
filters = None
scheme = None # owner scheme
def __init__(self, name, filters=[]):
self.name = name
self.filters = filters
for idx, filter_ in enumerate(self.filters):
filter_.idx = idx
filter_.filters = self
def sysfs_dir(self):
return os.path.join(self.scheme.sysfs_dir(), self.name)
def stage(self):
err = write_file(os.path.join(self.sysfs_dir(), 'nr_filters' ),
len(self.filters))
if err is not None :
return err
for filter_ in self.filters:
err = filter_.stage()
if err is not None :
return err
return None
class DamosDest:
id = None
weight = None
idx = None
dests = None # owner dests
def __init__(self, id=0, weight=0):
self.id = id
self.weight = weight
def sysfs_dir(self):
return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx)
def stage(self):
err = write_file(os.path.join(self.sysfs_dir(), 'id' ), self.id)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'weight' ), self.weight)
if err is not None :
return err
return None
class DamosDests:
dests = None
scheme = None # owner scheme
def __init__(self, dests=[]):
self.dests = dests
for idx, dest in enumerate(self.dests):
dest.idx = idx
dest.dests = self
def sysfs_dir(self):
return os.path.join(self.scheme.sysfs_dir(), 'dests' )
def stage(self):
err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests' ),
len(self.dests))
if err is not None :
return err
for dest in self.dests:
err = dest.stage()
if err is not None :
return err
return None
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 DamosTriedRegion:
def __init__(self, start, end, nr_accesses, age):
self.start = start
self.end = end
self.nr_accesses = nr_accesses
self.age = age
class Damos:
action = None
access_pattern = None
quota = None
watermarks = None
core_filters = None
ops_filters = None
filters = None
apply_interval_us = None
target_nid = None
dests = None
idx = None
context = None
tried_bytes = None
stats = None
tried_regions = None
def __init__(self, action='stat' , access_pattern=DamosAccessPattern(),
quota=DamosQuota(), watermarks=DamosWatermarks(),
core_filters=[], ops_filters=[], filters=[], target_nid=0,
dests=DamosDests(), 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.watermarks = watermarks
self.watermarks.scheme = self
self.core_filters = DamosFilters(name='core_filters' ,
filters=core_filters)
self.core_filters.scheme = self
self.ops_filters = DamosFilters(name='ops_filters' ,
filters=ops_filters)
self.ops_filters.scheme = self
self.filters = DamosFilters(name='filters' , filters=filters)
self.filters.scheme = self
self.target_nid = target_nid
self.dests = dests
self.dests.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 is not None :
return err
err = self.access_pattern.stage()
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us' ),
'%d' % self.apply_interval_us)
if err is not None :
return err
err = self.quota.stage()
if err is not None :
return err
err = self.watermarks.stage()
if err is not None :
return err
err = self.core_filters.stage()
if err is not None :
return err
err = self.ops_filters.stage()
if err is not None :
return err
err = self.filters.stage()
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'target_nid' ), '%d' %
self.target_nid)
if err is not None :
return err
err = self.dests.stage()
if err is not 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 is not None :
return err
return write_file(
os.path.join(self.sysfs_dir(), 'pid_target' ), self.pid)
class IntervalsGoal:
access_bp = None
aggrs = None
min_sample_us = None
max_sample_us = None
attrs = None # owner DamonAttrs
def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0):
self.access_bp = access_bp
self.aggrs = aggrs
self.min_sample_us = min_sample_us
self.max_sample_us = max_sample_us
def sysfs_dir(self):
return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal' )
def stage(self):
err = write_file(
os.path.join(self.sysfs_dir(), 'access_bp' ), self.access_bp)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'aggrs' ), self.aggrs)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us' ),
self.min_sample_us)
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us' ),
self.max_sample_us)
if err is not None :
return err
return None
class DamonAttrs:
sample_us = None
aggr_us = None
intervals_goal = None
update_us = None
min_nr_regions = None
max_nr_regions = None
context = None
def __init__(self, sample_us=5000, aggr_us=100000,
intervals_goal=IntervalsGoal(), update_us=1000000,
min_nr_regions=10, max_nr_regions=1000):
self.sample_us = sample_us
self.aggr_us = aggr_us
self.intervals_goal = intervals_goal
self.intervals_goal.attrs = self
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 is not None :
return err
err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us' ),
self.aggr_us)
if err is not None :
return err
err = self.intervals_goal.stage()
if err is not None :
return err
err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us' ),
self.update_us)
if err is not None :
return err
err = write_file(
os.path.join(self.nr_regions_range_sysfs_dir(), 'min' ),
self.min_nr_regions)
if err is not None :
return err
err = write_file(
os.path.join(self.nr_regions_range_sysfs_dir(), 'max' ),
self.max_nr_regions)
if err is not 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 is not None :
return err
err = self.monitoring_attrs.stage()
if err is not None :
return err
nr_targets_file = os.path.join(
self.sysfs_dir(), 'targets' , 'nr_targets' )
content, err = read_file(nr_targets_file)
if err is not None :
return err
if int(content) != len(self.targets):
err = write_file(nr_targets_file, '%d' % len(self.targets))
if err is not None :
return err
for target in self.targets:
err = target.stage()
if err is not None :
return err
nr_schemes_file = os.path.join(
self.sysfs_dir(), 'schemes' , 'nr_schemes' )
content, err = read_file(nr_schemes_file)
if err is not None :
return err
if int(content) != len(self.schemes):
err = write_file(nr_schemes_file, '%d' % len(self.schemes))
if err is not None :
return err
for scheme in self.schemes:
err = scheme.stage()
if err is not 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 is not None :
return err
if int(content) != len(self.contexts):
err = write_file(nr_contexts_file, '%d' % len(self.contexts))
if err is not None :
return err
for context in self.contexts:
err = context.stage()
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'state' ), 'on' )
if err is not None :
return err
self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid' ))
return err
def stop(self):
err = write_file(os.path.join(self.sysfs_dir(), 'state' ), 'off' )
return err
def update_schemes_tried_regions(self):
err = write_file(os.path.join(self.sysfs_dir(), 'state' ),
'update_schemes_tried_regions' )
if err is not None :
return err
for context in self.contexts:
for scheme in context.schemes:
tried_regions = []
tried_regions_dir = os.path.join(
scheme.sysfs_dir(), 'tried_regions' )
region_indices = []
for filename in os.listdir(
os.path.join(scheme.sysfs_dir(), 'tried_regions' )):
tried_region_dir = os.path.join(tried_regions_dir, filename)
if not os.path.isdir(tried_region_dir):
continue
region_indices.append(int(filename))
for region_idx in sorted(region_indices):
tried_region_dir = os.path.join(tried_regions_dir,
'%d' % region_idx)
region_values = []
for f in ['start' , 'end' , 'nr_accesses' , 'age' ]:
content, err = read_file(
os.path.join(tried_region_dir, f))
if err is not None :
return err
region_values.append(int(content))
tried_regions.append(DamosTriedRegion(*region_values))
scheme.tried_regions = tried_regions
def update_schemes_tried_bytes(self):
err = write_file(os.path.join(self.sysfs_dir(), 'state' ),
'update_schemes_tried_bytes' )
if err is not 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 is not 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 is not 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 is not None :
return err
stat_values.append(int(content))
scheme.stats = DamosStats(*stat_values)
def update_schemes_effective_quotas(self):
err = write_file(os.path.join(self.sysfs_dir(), 'state' ),
'update_schemes_effective_quotas' )
if err is not None :
return err
for context in self.contexts:
for scheme in context.schemes:
for goal in scheme.quota.goals:
content, err = read_file(
os.path.join(scheme.quota.sysfs_dir(),
'effective_bytes' ))
if err is not None :
return err
goal.effective_bytes = int(content)
return None
def commit(self):
nr_contexts_file = os.path.join(self.sysfs_dir(),
'contexts' , 'nr_contexts' )
content, err = read_file(nr_contexts_file)
if err is not None :
return err
if int(content) != len(self.contexts):
err = write_file(nr_contexts_file, '%d' % len(self.contexts))
if err is not None :
return err
for context in self.contexts:
err = context.stage()
if err is not None :
return err
err = write_file(os.path.join(self.sysfs_dir(), 'state' ), 'commit' )
return err
def commit_schemes_quota_goals(self):
for context in self.contexts:
for scheme in context.schemes:
for goal in scheme.quota.goals:
err = goal.stage()
if err is not None :
print('commit_schemes_quota_goals failed stagign: %s' %
err)
exit(1)
return write_file(os.path.join(self.sysfs_dir(), 'state' ),
'commit_schemes_quota_goals' )
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 is not None :
return err
for kdamond in self.kdamonds:
err = kdamond.start()
if err is not None :
return err
return None
def stop(self):
for kdamond in self.kdamonds:
err = kdamond.stop()
if err is not None :
return err
return None
Messung V0.5 C=99 H=92 G=95
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland