16954ff18STejun Heo#!/usr/bin/env drgn 26954ff18STejun Heo# 36954ff18STejun Heo# Copyright (C) 2019 Tejun Heo <tj@kernel.org> 46954ff18STejun Heo# Copyright (C) 2019 Facebook 56954ff18STejun Heo 66954ff18STejun Heodesc = """ 76954ff18STejun HeoThis is a drgn script to monitor the blk-iocost cgroup controller. 86954ff18STejun HeoSee the comment at the top of block/blk-iocost.c for more details. 96954ff18STejun HeoFor drgn, visit https://github.com/osandov/drgn. 106954ff18STejun Heo""" 116954ff18STejun Heo 126954ff18STejun Heoimport sys 136954ff18STejun Heoimport re 146954ff18STejun Heoimport time 156954ff18STejun Heoimport json 16b06f2d35STejun Heoimport math 176954ff18STejun Heo 186954ff18STejun Heoimport drgn 196954ff18STejun Heofrom drgn import container_of 206954ff18STejun Heofrom drgn.helpers.linux.list import list_for_each_entry,list_empty 216954ff18STejun Heofrom drgn.helpers.linux.radixtree import radix_tree_for_each,radix_tree_lookup 226954ff18STejun Heo 236954ff18STejun Heoimport argparse 246954ff18STejun Heoparser = argparse.ArgumentParser(description=desc, 256954ff18STejun Heo formatter_class=argparse.RawTextHelpFormatter) 266954ff18STejun Heoparser.add_argument('devname', metavar='DEV', 276954ff18STejun Heo help='Target block device name (e.g. sda)') 286954ff18STejun Heoparser.add_argument('--cgroup', action='append', metavar='REGEX', 296954ff18STejun Heo help='Regex for target cgroups, ') 306954ff18STejun Heoparser.add_argument('--interval', '-i', metavar='SECONDS', type=float, default=1, 31f4fe3ea6STejun Heo help='Monitoring interval in seconds (0 exits immediately ' 32f4fe3ea6STejun Heo 'after checking requirements)') 336954ff18STejun Heoparser.add_argument('--json', action='store_true', 346954ff18STejun Heo help='Output in json') 356954ff18STejun Heoargs = parser.parse_args() 366954ff18STejun Heo 376954ff18STejun Heodef err(s): 386954ff18STejun Heo print(s, file=sys.stderr, flush=True) 396954ff18STejun Heo sys.exit(1) 406954ff18STejun Heo 416954ff18STejun Heotry: 426954ff18STejun Heo blkcg_root = prog['blkcg_root'] 436954ff18STejun Heo plid = prog['blkcg_policy_iocost'].plid.value_() 446954ff18STejun Heoexcept: 456954ff18STejun Heo err('The kernel does not have iocost enabled') 466954ff18STejun Heo 476954ff18STejun HeoIOC_RUNNING = prog['IOC_RUNNING'].value_() 48a7863b34STejun HeoWEIGHT_ONE = prog['WEIGHT_ONE'].value_() 496954ff18STejun HeoVTIME_PER_SEC = prog['VTIME_PER_SEC'].value_() 506954ff18STejun HeoVTIME_PER_USEC = prog['VTIME_PER_USEC'].value_() 516954ff18STejun HeoAUTOP_SSD_FAST = prog['AUTOP_SSD_FAST'].value_() 526954ff18STejun HeoAUTOP_SSD_DFL = prog['AUTOP_SSD_DFL'].value_() 536954ff18STejun HeoAUTOP_SSD_QD1 = prog['AUTOP_SSD_QD1'].value_() 546954ff18STejun HeoAUTOP_HDD = prog['AUTOP_HDD'].value_() 556954ff18STejun Heo 566954ff18STejun Heoautop_names = { 576954ff18STejun Heo AUTOP_SSD_FAST: 'ssd_fast', 586954ff18STejun Heo AUTOP_SSD_DFL: 'ssd_dfl', 596954ff18STejun Heo AUTOP_SSD_QD1: 'ssd_qd1', 606954ff18STejun Heo AUTOP_HDD: 'hdd', 616954ff18STejun Heo} 626954ff18STejun Heo 636954ff18STejun Heoclass BlkgIterator: 64b74440d8SElijah Conners def __init__(self, root_blkcg, q_id, include_dying=False): 65b74440d8SElijah Conners self.include_dying = include_dying 66b74440d8SElijah Conners self.blkgs = [] 67b74440d8SElijah Conners self.walk(root_blkcg, q_id, '') 68b74440d8SElijah Conners 696954ff18STejun Heo def blkcg_name(blkcg): 706954ff18STejun Heo return blkcg.css.cgroup.kn.name.string_().decode('utf-8') 716954ff18STejun Heo 726954ff18STejun Heo def walk(self, blkcg, q_id, parent_path): 736954ff18STejun Heo if not self.include_dying and \ 746954ff18STejun Heo not (blkcg.css.flags.value_() & prog['CSS_ONLINE'].value_()): 756954ff18STejun Heo return 766954ff18STejun Heo 776954ff18STejun Heo name = BlkgIterator.blkcg_name(blkcg) 786954ff18STejun Heo path = parent_path + '/' + name if parent_path else name 796954ff18STejun Heo blkg = drgn.Object(prog, 'struct blkcg_gq', 809ea37e24STejun Heo address=radix_tree_lookup(blkcg.blkg_tree.address_of_(), q_id)) 816954ff18STejun Heo if not blkg.address_: 826954ff18STejun Heo return 836954ff18STejun Heo 846954ff18STejun Heo self.blkgs.append((path if path else '/', blkg)) 856954ff18STejun Heo 866954ff18STejun Heo for c in list_for_each_entry('struct blkcg', 876954ff18STejun Heo blkcg.css.children.address_of_(), 'css.sibling'): 886954ff18STejun Heo self.walk(c, q_id, path) 896954ff18STejun Heo 906954ff18STejun Heo def __iter__(self): 916954ff18STejun Heo return iter(self.blkgs) 926954ff18STejun Heo 936954ff18STejun Heoclass IocStat: 946954ff18STejun Heo def __init__(self, ioc): 956954ff18STejun Heo global autop_names 966954ff18STejun Heo 976954ff18STejun Heo self.enabled = ioc.enabled.value_() 986954ff18STejun Heo self.running = ioc.running.value_() == IOC_RUNNING 99b06f2d35STejun Heo self.period_ms = ioc.period_us.value_() / 1_000 1006954ff18STejun Heo self.period_at = ioc.period_at.value_() / 1_000_000 1016954ff18STejun Heo self.vperiod_at = ioc.period_at_vtime.value_() / VTIME_PER_SEC 102a7863b34STejun Heo self.vrate_pct = ioc.vtime_base_rate.value_() * 100 / VTIME_PER_USEC 1038e93c1acSChengming Zhou self.ivrate_pct = ioc.vtime_rate.counter.value_() * 100 / VTIME_PER_USEC 1046954ff18STejun Heo self.busy_level = ioc.busy_level.value_() 1056954ff18STejun Heo self.autop_idx = ioc.autop_idx.value_() 1066954ff18STejun Heo self.user_cost_model = ioc.user_cost_model.value_() 1076954ff18STejun Heo self.user_qos_params = ioc.user_qos_params.value_() 1086954ff18STejun Heo 1096954ff18STejun Heo if self.autop_idx in autop_names: 1106954ff18STejun Heo self.autop_name = autop_names[self.autop_idx] 1116954ff18STejun Heo else: 1126954ff18STejun Heo self.autop_name = '?' 1136954ff18STejun Heo 1146954ff18STejun Heo def dict(self, now): 1156954ff18STejun Heo return { 'device' : devname, 11621f3cfeaSTejun Heo 'timestamp' : now, 11721f3cfeaSTejun Heo 'enabled' : self.enabled, 11821f3cfeaSTejun Heo 'running' : self.running, 11921f3cfeaSTejun Heo 'period_ms' : self.period_ms, 12021f3cfeaSTejun Heo 'period_at' : self.period_at, 12121f3cfeaSTejun Heo 'period_vtime_at' : self.vperiod_at, 12221f3cfeaSTejun Heo 'busy_level' : self.busy_level, 1238e93c1acSChengming Zhou 'vrate_pct' : self.vrate_pct, 1248e93c1acSChengming Zhou 'ivrate_pct' : self.ivrate_pct, 1258e93c1acSChengming Zhou } 1266954ff18STejun Heo 1276954ff18STejun Heo def table_preamble_str(self): 1286954ff18STejun Heo state = ('RUN' if self.running else 'IDLE') if self.enabled else 'OFF' 1296954ff18STejun Heo output = f'{devname} {state:4} ' \ 1306954ff18STejun Heo f'per={self.period_ms}ms ' \ 1316954ff18STejun Heo f'cur_per={self.period_at:.3f}:v{self.vperiod_at:.3f} ' \ 1326954ff18STejun Heo f'busy={self.busy_level:+3} ' \ 1338e93c1acSChengming Zhou f'vrate={self.vrate_pct:6.2f}%:{self.ivrate_pct:6.2f}% ' \ 1346954ff18STejun Heo f'params={self.autop_name}' 1356954ff18STejun Heo if self.user_cost_model or self.user_qos_params: 1366954ff18STejun Heo output += f'({"C" if self.user_cost_model else ""}{"Q" if self.user_qos_params else ""})' 1376954ff18STejun Heo return output 1386954ff18STejun Heo 1396954ff18STejun Heo def table_header_str(self): 1406954ff18STejun Heo return f'{"":25} active {"weight":>9} {"hweight%":>13} {"inflt%":>6} ' \ 141*68392b00SChengming Zhou f'{"usage%":>6} {"wait":>7} {"debt":>7} {"delay":>7}' 1426954ff18STejun Heo 1436954ff18STejun Heoclass IocgStat: 1446954ff18STejun Heo def __init__(self, iocg): 1456954ff18STejun Heo ioc = iocg.ioc 1466954ff18STejun Heo blkg = iocg.pd.blkg 1476954ff18STejun Heo 1486954ff18STejun Heo self.is_active = not list_empty(iocg.active_list.address_of_()) 149a7863b34STejun Heo self.weight = iocg.weight.value_() / WEIGHT_ONE 150a7863b34STejun Heo self.active = iocg.active.value_() / WEIGHT_ONE 151a7863b34STejun Heo self.inuse = iocg.inuse.value_() / WEIGHT_ONE 152a7863b34STejun Heo self.hwa_pct = iocg.hweight_active.value_() * 100 / WEIGHT_ONE 153a7863b34STejun Heo self.hwi_pct = iocg.hweight_inuse.value_() * 100 / WEIGHT_ONE 154b06f2d35STejun Heo self.address = iocg.value_() 1556954ff18STejun Heo 1566954ff18STejun Heo vdone = iocg.done_vtime.counter.value_() 1576954ff18STejun Heo vtime = iocg.vtime.counter.value_() 1586954ff18STejun Heo vrate = ioc.vtime_rate.counter.value_() 1596954ff18STejun Heo period_vtime = ioc.period_us.value_() * vrate 1606954ff18STejun Heo if period_vtime: 1616954ff18STejun Heo self.inflight_pct = (vtime - vdone) * 100 / period_vtime 1626954ff18STejun Heo else: 1636954ff18STejun Heo self.inflight_pct = 0 1646954ff18STejun Heo 165a7863b34STejun Heo self.usage = (100 * iocg.usage_delta_us.value_() / 166a7863b34STejun Heo ioc.period_us.value_()) if self.active else 0 167*68392b00SChengming Zhou self.wait_ms = (iocg.stat.wait_us.value_() - 168*68392b00SChengming Zhou iocg.last_stat.wait_us.value_()) / 1000 1690b80f986STejun Heo self.debt_ms = iocg.abs_vdebt.value_() / VTIME_PER_USEC / 1000 170a7863b34STejun Heo if blkg.use_delay.counter.value_() != 0: 171b06f2d35STejun Heo self.delay_ms = blkg.delay_nsec.counter.value_() / 1_000_000 172a7863b34STejun Heo else: 173a7863b34STejun Heo self.delay_ms = 0 1746954ff18STejun Heo 1756954ff18STejun Heo def dict(self, now, path): 1766954ff18STejun Heo out = { 'cgroup' : path, 17721f3cfeaSTejun Heo 'timestamp' : now, 17821f3cfeaSTejun Heo 'is_active' : self.is_active, 17921f3cfeaSTejun Heo 'weight' : self.weight, 18021f3cfeaSTejun Heo 'weight_active' : self.active, 18121f3cfeaSTejun Heo 'weight_inuse' : self.inuse, 18221f3cfeaSTejun Heo 'hweight_active_pct' : self.hwa_pct, 18321f3cfeaSTejun Heo 'hweight_inuse_pct' : self.hwi_pct, 18421f3cfeaSTejun Heo 'inflight_pct' : self.inflight_pct, 185*68392b00SChengming Zhou 'usage_pct' : self.usage, 186*68392b00SChengming Zhou 'wait_ms' : self.wait_ms, 18721f3cfeaSTejun Heo 'debt_ms' : self.debt_ms, 18821f3cfeaSTejun Heo 'delay_ms' : self.delay_ms, 18921f3cfeaSTejun Heo 'address' : self.address } 1906954ff18STejun Heo return out 1916954ff18STejun Heo 1926954ff18STejun Heo def table_row_str(self, path): 1936954ff18STejun Heo out = f'{path[-28:]:28} ' \ 1946954ff18STejun Heo f'{"*" if self.is_active else " "} ' \ 195a7863b34STejun Heo f'{round(self.inuse):5}/{round(self.active):5} ' \ 1966954ff18STejun Heo f'{self.hwi_pct:6.2f}/{self.hwa_pct:6.2f} ' \ 1976954ff18STejun Heo f'{self.inflight_pct:6.2f} ' \ 198*68392b00SChengming Zhou f'{min(self.usage, 999):6.2f} ' \ 199*68392b00SChengming Zhou f'{self.wait_ms:7.2f} ' \ 200a7863b34STejun Heo f'{self.debt_ms:7.2f} ' \ 201*68392b00SChengming Zhou f'{self.delay_ms:7.2f}' 2026954ff18STejun Heo out = out.rstrip(':') 2036954ff18STejun Heo return out 2046954ff18STejun Heo 2056954ff18STejun Heo# handle args 2066954ff18STejun Heotable_fmt = not args.json 2076954ff18STejun Heointerval = args.interval 2086954ff18STejun Heodevname = args.devname 2096954ff18STejun Heo 2106954ff18STejun Heoif args.json: 2116954ff18STejun Heo table_fmt = False 2126954ff18STejun Heo 2136954ff18STejun Heore_str = None 2146954ff18STejun Heoif args.cgroup: 2156954ff18STejun Heo for r in args.cgroup: 2166954ff18STejun Heo if re_str is None: 2176954ff18STejun Heo re_str = r 2186954ff18STejun Heo else: 2196954ff18STejun Heo re_str += '|' + r 2206954ff18STejun Heo 2216954ff18STejun Heofilter_re = re.compile(re_str) if re_str else None 2226954ff18STejun Heo 2236954ff18STejun Heo# Locate the roots 2246954ff18STejun Heoq_id = None 2256954ff18STejun Heoroot_iocg = None 2266954ff18STejun Heoioc = None 2276954ff18STejun Heo 2289ea37e24STejun Heofor i, ptr in radix_tree_for_each(blkcg_root.blkg_tree.address_of_()): 2296954ff18STejun Heo blkg = drgn.Object(prog, 'struct blkcg_gq', address=ptr) 2306954ff18STejun Heo try: 2312eae9c49SChengming Zhou if devname == blkg.q.mq_kobj.parent.name.string_().decode('utf-8'): 2326954ff18STejun Heo q_id = blkg.q.id.value_() 2336954ff18STejun Heo if blkg.pd[plid]: 2346954ff18STejun Heo root_iocg = container_of(blkg.pd[plid], 'struct ioc_gq', 'pd') 2356954ff18STejun Heo ioc = root_iocg.ioc 2366954ff18STejun Heo break 2376954ff18STejun Heo except: 2386954ff18STejun Heo pass 2396954ff18STejun Heo 2406954ff18STejun Heoif ioc is None: 2416954ff18STejun Heo err(f'Could not find ioc for {devname}'); 2426954ff18STejun Heo 243f4fe3ea6STejun Heoif interval == 0: 244f4fe3ea6STejun Heo sys.exit(0) 245f4fe3ea6STejun Heo 2466954ff18STejun Heo# Keep printing 2476954ff18STejun Heowhile True: 2486954ff18STejun Heo now = time.time() 2496954ff18STejun Heo iocstat = IocStat(ioc) 2506954ff18STejun Heo output = '' 2516954ff18STejun Heo 2526954ff18STejun Heo if table_fmt: 2536954ff18STejun Heo output += '\n' + iocstat.table_preamble_str() 2546954ff18STejun Heo output += '\n' + iocstat.table_header_str() 2556954ff18STejun Heo else: 2566954ff18STejun Heo output += json.dumps(iocstat.dict(now)) 2576954ff18STejun Heo 2586954ff18STejun Heo for path, blkg in BlkgIterator(blkcg_root, q_id): 2596954ff18STejun Heo if filter_re and not filter_re.match(path): 2606954ff18STejun Heo continue 2616954ff18STejun Heo if not blkg.pd[plid]: 2626954ff18STejun Heo continue 2636954ff18STejun Heo 2646954ff18STejun Heo iocg = container_of(blkg.pd[plid], 'struct ioc_gq', 'pd') 2656954ff18STejun Heo iocg_stat = IocgStat(iocg) 2666954ff18STejun Heo 2676954ff18STejun Heo if not filter_re and not iocg_stat.is_active: 2686954ff18STejun Heo continue 2696954ff18STejun Heo 2706954ff18STejun Heo if table_fmt: 2716954ff18STejun Heo output += '\n' + iocg_stat.table_row_str(path) 2726954ff18STejun Heo else: 2736954ff18STejun Heo output += '\n' + json.dumps(iocg_stat.dict(now, path)) 2746954ff18STejun Heo 2756954ff18STejun Heo print(output) 2766954ff18STejun Heo sys.stdout.flush() 2776954ff18STejun Heo time.sleep(interval) 278