1# SPDX-License-Identifier: GPL-2.0 2 3import os 4 5sysfs_root = '/sys/kernel/mm/damon/admin' 6 7def write_file(path, string): 8 "Returns error string if failed, or None otherwise" 9 string = '%s' % string 10 try: 11 with open(path, 'w') as f: 12 f.write(string) 13 except Exception as e: 14 return '%s' % e 15 return None 16 17def read_file(path): 18 '''Returns the read content and error string. The read content is None if 19 the reading failed''' 20 try: 21 with open(path, 'r') as f: 22 return f.read(), None 23 except Exception as e: 24 return None, '%s' % e 25 26class DamosAccessPattern: 27 size = None 28 nr_accesses = None 29 age = None 30 scheme = None 31 32 def __init__(self, size=None, nr_accesses=None, age=None): 33 self.size = size 34 self.nr_accesses = nr_accesses 35 self.age = age 36 37 if self.size == None: 38 self.size = [0, 2**64 - 1] 39 if self.nr_accesses == None: 40 self.nr_accesses = [0, 2**64 - 1] 41 if self.age == None: 42 self.age = [0, 2**64 - 1] 43 44 def sysfs_dir(self): 45 return os.path.join(self.scheme.sysfs_dir(), 'access_pattern') 46 47 def stage(self): 48 err = write_file( 49 os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0]) 50 if err != None: 51 return err 52 err = write_file( 53 os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1]) 54 if err != None: 55 return err 56 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'), 57 self.nr_accesses[0]) 58 if err != None: 59 return err 60 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'), 61 self.nr_accesses[1]) 62 if err != None: 63 return err 64 err = write_file( 65 os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0]) 66 if err != None: 67 return err 68 err = write_file( 69 os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1]) 70 if err != None: 71 return err 72 73class Damos: 74 action = None 75 access_pattern = None 76 # todo: Support quotas, watermarks, stats, tried_regions 77 idx = None 78 context = None 79 tried_bytes = None 80 81 def __init__(self, action='stat', access_pattern=DamosAccessPattern()): 82 self.action = action 83 self.access_pattern = access_pattern 84 self.access_pattern.scheme = self 85 86 def sysfs_dir(self): 87 return os.path.join( 88 self.context.sysfs_dir(), 'schemes', '%d' % self.idx) 89 90 def stage(self): 91 err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action) 92 if err != None: 93 return err 94 err = self.access_pattern.stage() 95 if err != None: 96 return err 97 98 # disable quotas 99 err = write_file(os.path.join(self.sysfs_dir(), 'quotas', 'ms'), '0') 100 if err != None: 101 return err 102 err = write_file( 103 os.path.join(self.sysfs_dir(), 'quotas', 'bytes'), '0') 104 if err != None: 105 return err 106 107 # disable watermarks 108 err = write_file( 109 os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none') 110 if err != None: 111 return err 112 113 # disable filters 114 err = write_file( 115 os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0') 116 if err != None: 117 return err 118 119class DamonTarget: 120 pid = None 121 # todo: Support target regions if test is made 122 idx = None 123 context = None 124 125 def __init__(self, pid): 126 self.pid = pid 127 128 def sysfs_dir(self): 129 return os.path.join( 130 self.context.sysfs_dir(), 'targets', '%d' % self.idx) 131 132 def stage(self): 133 err = write_file( 134 os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0') 135 if err != None: 136 return err 137 return write_file( 138 os.path.join(self.sysfs_dir(), 'pid_target'), self.pid) 139 140class DamonAttrs: 141 sample_us = None 142 aggr_us = None 143 update_us = None 144 min_nr_regions = None 145 max_nr_regions = None 146 context = None 147 148 def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000, 149 min_nr_regions=10, max_nr_regions=1000): 150 self.sample_us = sample_us 151 self.aggr_us = aggr_us 152 self.update_us = update_us 153 self.min_nr_regions = min_nr_regions 154 self.max_nr_regions = max_nr_regions 155 156 def interval_sysfs_dir(self): 157 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs', 158 'intervals') 159 160 def nr_regions_range_sysfs_dir(self): 161 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs', 162 'nr_regions') 163 164 def stage(self): 165 err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'), 166 self.sample_us) 167 if err != None: 168 return err 169 err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'), 170 self.aggr_us) 171 if err != None: 172 return err 173 err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'), 174 self.update_us) 175 if err != None: 176 return err 177 178 err = write_file( 179 os.path.join(self.nr_regions_range_sysfs_dir(), 'min'), 180 self.min_nr_regions) 181 if err != None: 182 return err 183 184 err = write_file( 185 os.path.join(self.nr_regions_range_sysfs_dir(), 'max'), 186 self.max_nr_regions) 187 if err != None: 188 return err 189 190class DamonCtx: 191 ops = None 192 monitoring_attrs = None 193 targets = None 194 schemes = None 195 kdamond = None 196 idx = None 197 198 def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[], 199 schemes=[]): 200 self.ops = ops 201 self.monitoring_attrs = monitoring_attrs 202 self.monitoring_attrs.context = self 203 204 self.targets = targets 205 for idx, target in enumerate(self.targets): 206 target.idx = idx 207 target.context = self 208 209 self.schemes = schemes 210 for idx, scheme in enumerate(self.schemes): 211 scheme.idx = idx 212 scheme.context = self 213 214 def sysfs_dir(self): 215 return os.path.join(self.kdamond.sysfs_dir(), 'contexts', 216 '%d' % self.idx) 217 218 def stage(self): 219 err = write_file( 220 os.path.join(self.sysfs_dir(), 'operations'), self.ops) 221 if err != None: 222 return err 223 err = self.monitoring_attrs.stage() 224 if err != None: 225 return err 226 227 nr_targets_file = os.path.join( 228 self.sysfs_dir(), 'targets', 'nr_targets') 229 content, err = read_file(nr_targets_file) 230 if err != None: 231 return err 232 if int(content) != len(self.targets): 233 err = write_file(nr_targets_file, '%d' % len(self.targets)) 234 if err != None: 235 return err 236 for target in self.targets: 237 err = target.stage() 238 if err != None: 239 return err 240 241 nr_schemes_file = os.path.join( 242 self.sysfs_dir(), 'schemes', 'nr_schemes') 243 content, err = read_file(nr_schemes_file) 244 if int(content) != len(self.schemes): 245 err = write_file(nr_schemes_file, '%d' % len(self.schemes)) 246 if err != None: 247 return err 248 for scheme in self.schemes: 249 err = scheme.stage() 250 if err != None: 251 return err 252 return None 253 254class Kdamond: 255 state = None 256 pid = None 257 contexts = None 258 idx = None # index of this kdamond between siblings 259 kdamonds = None # parent 260 261 def __init__(self, contexts=[]): 262 self.contexts = contexts 263 for idx, context in enumerate(self.contexts): 264 context.idx = idx 265 context.kdamond = self 266 267 def sysfs_dir(self): 268 return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx) 269 270 def start(self): 271 nr_contexts_file = os.path.join(self.sysfs_dir(), 272 'contexts', 'nr_contexts') 273 content, err = read_file(nr_contexts_file) 274 if err != None: 275 return err 276 if int(content) != len(self.contexts): 277 err = write_file(nr_contexts_file, '%d' % len(self.contexts)) 278 if err != None: 279 return err 280 281 for context in self.contexts: 282 err = context.stage() 283 if err != None: 284 return err 285 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on') 286 return err 287 288 def update_schemes_tried_bytes(self): 289 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 290 'update_schemes_tried_bytes') 291 if err != None: 292 return err 293 for context in self.contexts: 294 for scheme in context.schemes: 295 content, err = read_file(os.path.join(scheme.sysfs_dir(), 296 'tried_regions', 'total_bytes')) 297 if err != None: 298 return err 299 scheme.tried_bytes = int(content) 300 301class Kdamonds: 302 kdamonds = [] 303 304 def __init__(self, kdamonds=[]): 305 self.kdamonds = kdamonds 306 for idx, kdamond in enumerate(self.kdamonds): 307 kdamond.idx = idx 308 kdamond.kdamonds = self 309 310 def sysfs_dir(self): 311 return os.path.join(sysfs_root, 'kdamonds') 312 313 def start(self): 314 err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'), 315 '%s' % len(self.kdamonds)) 316 if err != None: 317 return err 318 for kdamond in self.kdamonds: 319 err = kdamond.start() 320 if err != None: 321 return err 322 return None 323