1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging, os, re, time 6from autotest_lib.client.bin import fio_util, test, utils 7from autotest_lib.client.common_lib import error 8 9 10class hardware_StorageFio(test.test): 11 """ 12 Runs several fio jobs and reports results. 13 14 fio (flexible I/O tester) is an I/O tool for benchmark and stress/hardware 15 verification. 16 17 """ 18 19 version = 7 20 DEFAULT_FILE_SIZE = 1024 * 1024 * 1024 21 VERIFY_OPTION = 'v' 22 23 # Initialize fail counter used to determine test pass/fail. 24 _fail_count = 0 25 26 def __get_disk_size(self): 27 """Return the size in bytes of the device pointed to by __filename""" 28 self.__filesize = utils.get_disk_size(self.__filename) 29 30 if not self.__filesize: 31 raise error.TestNAError( 32 'Unable to find the partition %s, please plug in a USB ' 33 'flash drive and a SD card for testing external storage' % 34 self.__filename) 35 36 37 def __get_device_description(self): 38 """Get the device vendor and model name as its description""" 39 40 # Find the block device in sysfs. For example, a card read device may 41 # be in /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host4/ 42 # target4:0:0/4:0:0:0/block/sdb. 43 # Then read the vendor and model name in its grand-parent directory. 44 45 # Obtain the device name by stripping the partition number. 46 # For example, sda3 => sda; mmcblk1p3 => mmcblk1, nvme0n1p3 => nvme0n1. 47 device = re.match(r'.*(sd[a-z]|mmcblk[0-9]+|nvme[0-9]+n[0-9]+)p?[0-9]+', 48 self.__filename).group(1) 49 findsys = utils.run('find /sys/devices -name %s' % device) 50 device_path = findsys.stdout.rstrip() 51 52 if "nvme" in device: 53 dir_path = utils.run('dirname %s' % device_path).stdout.rstrip() 54 model_file = '%s/model' % dir_path 55 if os.path.exists(model_file): 56 self.__description = utils.read_one_line(model_file).strip() 57 else: 58 self.__description = '' 59 else: 60 vendor_file = device_path.replace('block/%s' % device, 'vendor') 61 model_file = device_path.replace('block/%s' % device, 'model') 62 if os.path.exists(vendor_file) and os.path.exists(model_file): 63 vendor = utils.read_one_line(vendor_file).strip() 64 model = utils.read_one_line(model_file).strip() 65 self.__description = vendor + ' ' + model 66 else: 67 self.__description = '' 68 69 70 def initialize(self, dev='', filesize=DEFAULT_FILE_SIZE): 71 """ 72 Set up local variables. 73 74 @param dev: block device / file to test. 75 Spare partition on root device by default 76 @param filesize: size of the file. 0 means whole partition. 77 by default, 1GB. 78 """ 79 if dev != '' and (os.path.isfile(dev) or not os.path.exists(dev)): 80 if filesize == 0: 81 raise error.TestError( 82 'Nonzero file size is required to test file systems') 83 self.__filename = dev 84 self.__filesize = filesize 85 self.__description = '' 86 return 87 88 if not dev: 89 dev = utils.get_fixed_dst_drive() 90 91 if dev == utils.get_root_device(): 92 if filesize == 0: 93 raise error.TestError( 94 'Using the root device as a whole is not allowed') 95 else: 96 self.__filename = utils.get_free_root_partition() 97 elif filesize != 0: 98 # Use the first partition of the external drive 99 if dev[5:7] == 'sd': 100 self.__filename = dev + '1' 101 else: 102 self.__filename = dev + 'p1' 103 else: 104 self.__filename = dev 105 self.__get_disk_size() 106 self.__get_device_description() 107 108 # Restrict test to use a given file size, default 1GiB 109 if filesize != 0: 110 self.__filesize = min(self.__filesize, filesize) 111 112 self.__verify_only = False 113 114 logging.info('filename: %s', self.__filename) 115 logging.info('filesize: %d', self.__filesize) 116 117 def run_once(self, dev='', quicktest=False, requirements=None, 118 integrity=False, wait=60 * 60 * 72): 119 """ 120 Runs several fio jobs and reports results. 121 122 @param dev: block device to test 123 @param quicktest: short test 124 @param requirements: list of jobs for fio to run 125 @param integrity: test to check data integrity 126 @param wait: seconds to wait between a write and subsequent verify 127 128 """ 129 130 if requirements is not None: 131 pass 132 elif quicktest: 133 requirements = [ 134 ('1m_write', []), 135 ('16k_read', []) 136 ] 137 elif integrity: 138 requirements = [ 139 ('8k_async_randwrite', []), 140 ('8k_async_randwrite', [self.VERIFY_OPTION]) 141 ] 142 elif dev in ['', utils.get_root_device()]: 143 requirements = [ 144 ('surfing', []), 145 ('boot', []), 146 ('login', []), 147 ('seq_read', []), 148 ('seq_write', []), 149 ('16k_read', []), 150 ('16k_write', []), 151 ('1m_stress', []), 152 ] 153 else: 154 # TODO(waihong@): Add more test cases for external storage 155 requirements = [ 156 ('seq_read', []), 157 ('seq_write', []), 158 ('16k_read', []), 159 ('16k_write', []), 160 ('1m_stress', []), 161 ] 162 163 results = {} 164 for job, options in requirements: 165 # Keys are labeled according to the test case name, which is 166 # unique per run, so they cannot clash 167 if self.VERIFY_OPTION in options: 168 time.sleep(wait) 169 self.__verify_only = True 170 else: 171 self.__verify_only = False 172 env_vars = ' '.join( 173 ['FILENAME=' + self.__filename, 174 'FILESIZE=' + str(self.__filesize), 175 'VERIFY_ONLY=' + str(int(self.__verify_only)) 176 ]) 177 job_file = os.path.join(self.bindir, job) 178 results.update(fio_util.fio_runner(self, job_file, env_vars)) 179 180 # Output keys relevant to the performance, larger filesize will run 181 # slower, and sda5 should be slightly slower than sda3 on a rotational 182 # disk 183 self.write_test_keyval({'filesize': self.__filesize, 184 'filename': self.__filename, 185 'device': self.__description}) 186 logging.info('Device Description: %s', self.__description) 187 self.write_perf_keyval(results) 188 for k, v in results.iteritems(): 189 if k.endswith('_error'): 190 self._fail_count += int(v) 191 if self._fail_count > 0: 192 raise error.TestFail('%s failed verifications' % 193 str(self._fail_count)) 194