ALSA: dice: disable double_pcm_frames mode for M-Audio Profire 610, 2626 and Avid...
[linux-2.6-microblaze.git] / tools / cgroup / iocost_coef_gen.py
1 #!/usr/bin/env python3
2 #
3 # Copyright (C) 2019 Tejun Heo <tj@kernel.org>
4 # Copyright (C) 2019 Andy Newell <newella@fb.com>
5 # Copyright (C) 2019 Facebook
6
7 desc = """
8 Generate linear IO cost model coefficients used by the blk-iocost
9 controller.  If the target raw testdev is specified, destructive tests
10 are performed against the whole device; otherwise, on
11 ./iocost-coef-fio.testfile.  The result can be written directly to
12 /sys/fs/cgroup/io.cost.model.
13
14 On high performance devices, --numjobs > 1 is needed to achieve
15 saturation.
16
17 See Documentation/admin-guide/cgroup-v2.rst and block/blk-iocost.c
18 for more details.
19 """
20
21 import argparse
22 import re
23 import json
24 import glob
25 import os
26 import sys
27 import atexit
28 import shutil
29 import tempfile
30 import subprocess
31
32 parser = argparse.ArgumentParser(description=desc,
33                                  formatter_class=argparse.RawTextHelpFormatter)
34 parser.add_argument('--testdev', metavar='DEV',
35                     help='Raw block device to use for testing, ignores --testfile-size')
36 parser.add_argument('--testfile-size-gb', type=float, metavar='GIGABYTES', default=16,
37                     help='Testfile size in gigabytes (default: %(default)s)')
38 parser.add_argument('--duration', type=int, metavar='SECONDS', default=120,
39                     help='Individual test run duration in seconds (default: %(default)s)')
40 parser.add_argument('--seqio-block-mb', metavar='MEGABYTES', type=int, default=128,
41                     help='Sequential test block size in megabytes (default: %(default)s)')
42 parser.add_argument('--seq-depth', type=int, metavar='DEPTH', default=64,
43                     help='Sequential test queue depth (default: %(default)s)')
44 parser.add_argument('--rand-depth', type=int, metavar='DEPTH', default=64,
45                     help='Random test queue depth (default: %(default)s)')
46 parser.add_argument('--numjobs', type=int, metavar='JOBS', default=1,
47                     help='Number of parallel fio jobs to run (default: %(default)s)')
48 parser.add_argument('--quiet', action='store_true')
49 parser.add_argument('--verbose', action='store_true')
50
51 def info(msg):
52     if not args.quiet:
53         print(msg)
54
55 def dbg(msg):
56     if args.verbose and not args.quiet:
57         print(msg)
58
59 # determine ('DEVNAME', 'MAJ:MIN') for @path
60 def dir_to_dev(path):
61     # find the block device the current directory is on
62     devname = subprocess.run(f'findmnt -nvo SOURCE -T{path}',
63                              stdout=subprocess.PIPE, shell=True).stdout
64     devname = os.path.basename(devname).decode('utf-8').strip()
65
66     # partition -> whole device
67     parents = glob.glob('/sys/block/*/' + devname)
68     if len(parents):
69         devname = os.path.basename(os.path.dirname(parents[0]))
70     rdev = os.stat(f'/dev/{devname}').st_rdev
71     return (devname, f'{os.major(rdev)}:{os.minor(rdev)}')
72
73 def create_testfile(path, size):
74     global args
75
76     if os.path.isfile(path) and os.stat(path).st_size == size:
77         return
78
79     info(f'Creating testfile {path}')
80     subprocess.check_call(f'rm -f {path}', shell=True)
81     subprocess.check_call(f'touch {path}', shell=True)
82     subprocess.call(f'chattr +C {path}', shell=True)
83     subprocess.check_call(
84         f'pv -s {size} -pr /dev/urandom {"-q" if args.quiet else ""} | '
85         f'dd of={path} count={size} '
86         f'iflag=count_bytes,fullblock oflag=direct bs=16M status=none',
87         shell=True)
88
89 def run_fio(testfile, duration, iotype, iodepth, blocksize, jobs):
90     global args
91
92     eta = 'never' if args.quiet else 'always'
93     outfile = tempfile.NamedTemporaryFile()
94     cmd = (f'fio --direct=1 --ioengine=libaio --name=coef '
95            f'--filename={testfile} --runtime={round(duration)} '
96            f'--readwrite={iotype} --iodepth={iodepth} --blocksize={blocksize} '
97            f'--eta={eta} --output-format json --output={outfile.name} '
98            f'--time_based --numjobs={jobs}')
99     if args.verbose:
100         dbg(f'Running {cmd}')
101     subprocess.check_call(cmd, shell=True)
102     with open(outfile.name, 'r') as f:
103         d = json.loads(f.read())
104     return sum(j['read']['bw_bytes'] + j['write']['bw_bytes'] for j in d['jobs'])
105
106 def restore_elevator_nomerges():
107     global elevator_path, nomerges_path, elevator, nomerges
108
109     info(f'Restoring elevator to {elevator} and nomerges to {nomerges}')
110     with open(elevator_path, 'w') as f:
111         f.write(elevator)
112     with open(nomerges_path, 'w') as f:
113         f.write(nomerges)
114
115
116 args = parser.parse_args()
117
118 missing = False
119 for cmd in [ 'findmnt', 'pv', 'dd', 'fio' ]:
120     if not shutil.which(cmd):
121         print(f'Required command "{cmd}" is missing', file=sys.stderr)
122         missing = True
123 if missing:
124     sys.exit(1)
125
126 if args.testdev:
127     devname = os.path.basename(args.testdev)
128     rdev = os.stat(f'/dev/{devname}').st_rdev
129     devno = f'{os.major(rdev)}:{os.minor(rdev)}'
130     testfile = f'/dev/{devname}'
131     info(f'Test target: {devname}({devno})')
132 else:
133     devname, devno = dir_to_dev('.')
134     testfile = 'iocost-coef-fio.testfile'
135     testfile_size = int(args.testfile_size_gb * 2 ** 30)
136     create_testfile(testfile, testfile_size)
137     info(f'Test target: {testfile} on {devname}({devno})')
138
139 elevator_path = f'/sys/block/{devname}/queue/scheduler'
140 nomerges_path = f'/sys/block/{devname}/queue/nomerges'
141
142 with open(elevator_path, 'r') as f:
143     elevator = re.sub(r'.*\[(.*)\].*', r'\1', f.read().strip())
144 with open(nomerges_path, 'r') as f:
145     nomerges = f.read().strip()
146
147 info(f'Temporarily disabling elevator and merges')
148 atexit.register(restore_elevator_nomerges)
149 with open(elevator_path, 'w') as f:
150     f.write('none')
151 with open(nomerges_path, 'w') as f:
152     f.write('1')
153
154 info('Determining rbps...')
155 rbps = run_fio(testfile, args.duration, 'read',
156                1, args.seqio_block_mb * (2 ** 20), args.numjobs)
157 info(f'\nrbps={rbps}, determining rseqiops...')
158 rseqiops = round(run_fio(testfile, args.duration, 'read',
159                          args.seq_depth, 4096, args.numjobs) / 4096)
160 info(f'\nrseqiops={rseqiops}, determining rrandiops...')
161 rrandiops = round(run_fio(testfile, args.duration, 'randread',
162                           args.rand_depth, 4096, args.numjobs) / 4096)
163 info(f'\nrrandiops={rrandiops}, determining wbps...')
164 wbps = run_fio(testfile, args.duration, 'write',
165                1, args.seqio_block_mb * (2 ** 20), args.numjobs)
166 info(f'\nwbps={wbps}, determining wseqiops...')
167 wseqiops = round(run_fio(testfile, args.duration, 'write',
168                          args.seq_depth, 4096, args.numjobs) / 4096)
169 info(f'\nwseqiops={wseqiops}, determining wrandiops...')
170 wrandiops = round(run_fio(testfile, args.duration, 'randwrite',
171                           args.rand_depth, 4096, args.numjobs) / 4096)
172 info(f'\nwrandiops={wrandiops}')
173 restore_elevator_nomerges()
174 atexit.unregister(restore_elevator_nomerges)
175 info('')
176
177 print(f'{devno} rbps={rbps} rseqiops={rseqiops} rrandiops={rrandiops} '
178       f'wbps={wbps} wseqiops={wseqiops} wrandiops={wrandiops}')