3 # Copyright (C) 2019 Tejun Heo <tj@kernel.org>
4 # Copyright (C) 2019 Facebook
7 This is a drgn script to monitor the blk-iocost cgroup controller.
8 See the comment at the top of block/blk-iocost.c for more details.
9 For drgn, visit https://github.com/osandov/drgn.
19 from drgn
import container_of
20 from drgn
.helpers
.linux
.list import list_for_each_entry
,list_empty
21 from drgn
.helpers
.linux
.radixtree
import radix_tree_for_each
,radix_tree_lookup
24 parser
= argparse
.ArgumentParser(description
=desc
,
25 formatter_class
=argparse
.RawTextHelpFormatter
)
26 parser
.add_argument('devname', metavar
='DEV',
27 help='Target block device name (e.g. sda)')
28 parser
.add_argument('--cgroup', action
='append', metavar
='REGEX',
29 help='Regex for target cgroups, ')
30 parser
.add_argument('--interval', '-i', metavar
='SECONDS', type=float, default
=1,
31 help='Monitoring interval in seconds (0 exits immediately '
32 'after checking requirements)')
33 parser
.add_argument('--json', action
='store_true',
34 help='Output in json')
35 args
= parser
.parse_args()
38 print(s
, file=sys
.stderr
, flush
=True)
42 blkcg_root
= prog
['blkcg_root']
43 plid
= prog
['blkcg_policy_iocost'].plid
.value_()
45 err('The kernel does not have iocost enabled')
47 IOC_RUNNING
= prog
['IOC_RUNNING'].value_()
48 WEIGHT_ONE
= prog
['WEIGHT_ONE'].value_()
49 VTIME_PER_SEC
= prog
['VTIME_PER_SEC'].value_()
50 VTIME_PER_USEC
= prog
['VTIME_PER_USEC'].value_()
51 AUTOP_SSD_FAST
= prog
['AUTOP_SSD_FAST'].value_()
52 AUTOP_SSD_DFL
= prog
['AUTOP_SSD_DFL'].value_()
53 AUTOP_SSD_QD1
= prog
['AUTOP_SSD_QD1'].value_()
54 AUTOP_HDD
= prog
['AUTOP_HDD'].value_()
57 AUTOP_SSD_FAST
: 'ssd_fast',
58 AUTOP_SSD_DFL
: 'ssd_dfl',
59 AUTOP_SSD_QD1
: 'ssd_qd1',
64 def blkcg_name(blkcg
):
65 return blkcg
.css
.cgroup
.kn
.name
.string_().decode('utf-8')
67 def walk(self
, blkcg
, q_id
, parent_path
):
68 if not self
.include_dying
and \
69 not (blkcg
.css
.flags
.value_() & prog
['CSS_ONLINE'].value_()):
72 name
= BlkgIterator
.blkcg_name(blkcg
)
73 path
= parent_path
+ '/' + name
if parent_path
else name
74 blkg
= drgn
.Object(prog
, 'struct blkcg_gq',
75 address
=radix_tree_lookup(blkcg
.blkg_tree
.address_of_(), q_id
))
79 self
.blkgs
.append((path
if path
else '/', blkg
))
81 for c
in list_for_each_entry('struct blkcg',
82 blkcg
.css
.children
.address_of_(), 'css.sibling'):
83 self
.walk(c
, q_id
, path
)
85 def __init__(self
, root_blkcg
, q_id
, include_dying
=False):
86 self
.include_dying
= include_dying
88 self
.walk(root_blkcg
, q_id
, '')
91 return iter(self
.blkgs
)
94 def __init__(self
, ioc
):
97 self
.enabled
= ioc
.enabled
.value_()
98 self
.running
= ioc
.running
.value_() == IOC_RUNNING
99 self
.period_ms
= ioc
.period_us
.value_() / 1_000
100 self
.period_at
= ioc
.period_at
.value_() / 1_000_000
101 self
.vperiod_at
= ioc
.period_at_vtime
.value_() / VTIME_PER_SEC
102 self
.vrate_pct
= ioc
.vtime_base_rate
.value_() * 100 / VTIME_PER_USEC
103 self
.busy_level
= ioc
.busy_level
.value_()
104 self
.autop_idx
= ioc
.autop_idx
.value_()
105 self
.user_cost_model
= ioc
.user_cost_model
.value_()
106 self
.user_qos_params
= ioc
.user_qos_params
.value_()
108 if self
.autop_idx
in autop_names
:
109 self
.autop_name
= autop_names
[self
.autop_idx
]
111 self
.autop_name
= '?'
114 return { 'device' : devname
,
116 'enabled' : self
.enabled
,
117 'running' : self
.running
,
118 'period_ms' : self
.period_ms
,
119 'period_at' : self
.period_at
,
120 'period_vtime_at' : self
.vperiod_at
,
121 'busy_level' : self
.busy_level
,
122 'vrate_pct' : self
.vrate_pct
, }
124 def table_preamble_str(self
):
125 state
= ('RUN' if self
.running
else 'IDLE') if self
.enabled
else 'OFF'
126 output
= f
'{devname} {state:4} ' \
127 f
'per={self.period_ms}ms ' \
128 f
'cur_per={self.period_at:.3f}:v{self.vperiod_at:.3f} ' \
129 f
'busy={self.busy_level:+3} ' \
130 f
'vrate={self.vrate_pct:6.2f}% ' \
131 f
'params={self.autop_name}'
132 if self
.user_cost_model
or self
.user_qos_params
:
133 output
+= f
'({"C" if self.user_cost_model else ""}{"Q" if self.user_qos_params else ""})'
136 def table_header_str(self
):
137 return f
'{"":25} active {"weight":>9} {"hweight%":>13} {"inflt%":>6} ' \
138 f
'{"debt":>7} {"delay":>7} {"usage%"}'
141 def __init__(self
, iocg
):
145 self
.is_active
= not list_empty(iocg
.active_list
.address_of_())
146 self
.weight
= iocg
.weight
.value_() / WEIGHT_ONE
147 self
.active
= iocg
.active
.value_() / WEIGHT_ONE
148 self
.inuse
= iocg
.inuse
.value_() / WEIGHT_ONE
149 self
.hwa_pct
= iocg
.hweight_active
.value_() * 100 / WEIGHT_ONE
150 self
.hwi_pct
= iocg
.hweight_inuse
.value_() * 100 / WEIGHT_ONE
151 self
.address
= iocg
.value_()
153 vdone
= iocg
.done_vtime
.counter
.value_()
154 vtime
= iocg
.vtime
.counter
.value_()
155 vrate
= ioc
.vtime_rate
.counter
.value_()
156 period_vtime
= ioc
.period_us
.value_() * vrate
158 self
.inflight_pct
= (vtime
- vdone
) * 100 / period_vtime
160 self
.inflight_pct
= 0
162 self
.usage
= (100 * iocg
.usage_delta_us
.value_() /
163 ioc
.period_us
.value_()) if self
.active
else 0
164 self
.debt_ms
= iocg
.abs_vdebt
.value_() / VTIME_PER_USEC
/ 1000
165 if blkg
.use_delay
.counter
.value_() != 0:
166 self
.delay_ms
= blkg
.delay_nsec
.counter
.value_() / 1_000_000
170 def dict(self
, now
, path
):
171 out
= { 'cgroup' : path
,
173 'is_active' : self
.is_active
,
174 'weight' : self
.weight
,
175 'weight_active' : self
.active
,
176 'weight_inuse' : self
.inuse
,
177 'hweight_active_pct' : self
.hwa_pct
,
178 'hweight_inuse_pct' : self
.hwi_pct
,
179 'inflight_pct' : self
.inflight_pct
,
180 'debt_ms' : self
.debt_ms
,
181 'delay_ms' : self
.delay_ms
,
182 'usage_pct' : self
.usage
,
183 'address' : self
.address
}
186 def table_row_str(self
, path
):
187 out
= f
'{path[-28:]:28} ' \
188 f
'{"*" if self.is_active else " "} ' \
189 f
'{round(self.inuse):5}/{round(self.active):5} ' \
190 f
'{self.hwi_pct:6.2f}/{self.hwa_pct:6.2f} ' \
191 f
'{self.inflight_pct:6.2f} ' \
192 f
'{self.debt_ms:7.2f} ' \
193 f
'{self.delay_ms:7.2f} '\
194 f
'{min(self.usage, 999):6.2f}'
195 out
= out
.rstrip(':')
199 table_fmt
= not args
.json
200 interval
= args
.interval
201 devname
= args
.devname
208 for r
in args
.cgroup
:
214 filter_re
= re
.compile(re_str
) if re_str
else None
221 for i
, ptr
in radix_tree_for_each(blkcg_root
.blkg_tree
.address_of_()):
222 blkg
= drgn
.Object(prog
, 'struct blkcg_gq', address
=ptr
)
224 if devname
== blkg
.q
.kobj
.parent
.name
.string_().decode('utf-8'):
225 q_id
= blkg
.q
.id.value_()
227 root_iocg
= container_of(blkg
.pd
[plid
], 'struct ioc_gq', 'pd')
234 err(f
'Could not find ioc for {devname}');
242 iocstat
= IocStat(ioc
)
246 output
+= '\n' + iocstat
.table_preamble_str()
247 output
+= '\n' + iocstat
.table_header_str()
249 output
+= json
.dumps(iocstat
.dict(now
))
251 for path
, blkg
in BlkgIterator(blkcg_root
, q_id
):
252 if filter_re
and not filter_re
.match(path
):
254 if not blkg
.pd
[plid
]:
257 iocg
= container_of(blkg
.pd
[plid
], 'struct ioc_gq', 'pd')
258 iocg_stat
= IocgStat(iocg
)
260 if not filter_re
and not iocg_stat
.is_active
:
264 output
+= '\n' + iocg_stat
.table_row_str(path
)
266 output
+= '\n' + json
.dumps(iocg_stat
.dict(now
, path
))