1 #!/usr/bin/env python3 2 # 3 # Copyright (C) 2019 Tejun Heo <tj@kernel.org> 4 # Copyright (C) 2019 Andy Newell <newella@fb.co 5 # Copyright (C) 2019 Facebook 6 7 desc = """ 8 Generate linear IO cost model coefficients use 9 controller. If the target raw testdev is spec 10 are performed against the whole device; otherw 11 ./iocost-coef-fio.testfile. The result can be 12 /sys/fs/cgroup/io.cost.model. 13 14 On high performance devices, --numjobs > 1 is 15 saturation. 16 17 See Documentation/admin-guide/cgroup-v2.rst an 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=d 33 formatter_cla 34 parser.add_argument('--testdev', metavar='DEV' 35 help='Raw block device to 36 parser.add_argument('--testfile-size-gb', type 37 help='Testfile size in gig 38 parser.add_argument('--duration', type=int, me 39 help='Individual test run 40 parser.add_argument('--seqio-block-mb', metava 41 help='Sequential test bloc 42 parser.add_argument('--seq-depth', type=int, m 43 help='Sequential test queu 44 parser.add_argument('--rand-depth', type=int, 45 help='Random test queue de 46 parser.add_argument('--numjobs', type=int, met 47 help='Number of parallel f 48 parser.add_argument('--quiet', action='store_t 49 parser.add_argument('--verbose', action='store 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 direct 62 devname = subprocess.run(f'findmnt -nvo SO 63 stdout=subprocess 64 devname = os.path.basename(devname).decode 65 66 # partition -> whole device 67 parents = glob.glob('/sys/block/*/' + devn 68 if len(parents): 69 devname = os.path.basename(os.path.dir 70 rdev = os.stat(f'/dev/{devname}').st_rdev 71 return (devname, f'{os.major(rdev)}:{os.mi 72 73 def create_testfile(path, size): 74 global args 75 76 if os.path.isfile(path) and os.stat(path). 77 return 78 79 info(f'Creating testfile {path}') 80 subprocess.check_call(f'rm -f {path}', she 81 subprocess.check_call(f'touch {path}', she 82 subprocess.call(f'chattr +C {path}', shell 83 subprocess.check_call( 84 f'pv -s {size} -pr /dev/urandom {"-q" 85 f'dd of={path} count={size} ' 86 f'iflag=count_bytes,fullblock oflag=di 87 shell=True) 88 89 def run_fio(testfile, duration, iotype, iodept 90 global args 91 92 eta = 'never' if args.quiet else 'always' 93 outfile = tempfile.NamedTemporaryFile() 94 cmd = (f'fio --direct=1 --ioengine=libaio 95 f'--filename={testfile} --runtime={ 96 f'--readwrite={iotype} --iodepth={i 97 f'--eta={eta} --output-format json 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['writ 105 106 def restore_elevator_nomerges(): 107 global elevator_path, nomerges_path, eleva 108 109 info(f'Restoring elevator to {elevator} an 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 mi 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 136 create_testfile(testfile, testfile_size) 137 info(f'Test target: {testfile} on {devname 138 139 elevator_path = f'/sys/block/{devname}/queue/s 140 nomerges_path = f'/sys/block/{devname}/queue/n 141 142 with open(elevator_path, 'r') as f: 143 elevator = re.sub(r'.*\[(.*)\].*', r'\1', 144 with open(nomerges_path, 'r') as f: 145 nomerges = f.read().strip() 146 147 info(f'Temporarily disabling elevator and merg 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 ** 157 info(f'\nrbps={rbps}, determining rseqiops...' 158 rseqiops = round(run_fio(testfile, args.durati 159 args.seq_depth, 4096, 160 info(f'\nrseqiops={rseqiops}, determining rran 161 rrandiops = round(run_fio(testfile, args.durat 162 args.rand_depth, 409 163 info(f'\nrrandiops={rrandiops}, determining wb 164 wbps = run_fio(testfile, args.duration, 'write 165 1, args.seqio_block_mb * (2 ** 166 info(f'\nwbps={wbps}, determining wseqiops...' 167 wseqiops = round(run_fio(testfile, args.durati 168 args.seq_depth, 4096, 169 info(f'\nwseqiops={wseqiops}, determining wran 170 wrandiops = round(run_fio(testfile, args.durat 171 args.rand_depth, 409 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 178 f'wbps={wbps} wseqiops={wseqiops} wrandi
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.