Home | History | Annotate | Download | only in hardware_StorageFio
      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 
      5 import logging, os, re, time
      6 from autotest_lib.client.bin import fio_util, test, utils
      7 from autotest_lib.client.common_lib import error
      8 
      9 
     10 class 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 = os.path.basename(
     48             re.sub('(sd[a-z]|mmcblk[0-9]+p|nvme[0-9]+n[0-9]+p)[0-9]+', '\\1', self.__filename))
     49         findsys = utils.run('find /sys/devices -name %s' % device)
     50         device_path = findsys.stdout.rstrip()
     51 
     52         vendor_file = device_path.replace('block/%s' % device, 'vendor')
     53         model_file = device_path.replace('block/%s' % device, 'model')
     54         if os.path.exists(vendor_file) and os.path.exists(model_file):
     55             vendor = utils.read_one_line(vendor_file).strip()
     56             model = utils.read_one_line(model_file).strip()
     57             self.__description = vendor + ' ' + model
     58         else:
     59             self.__description = ''
     60 
     61 
     62     def initialize(self, dev='', filesize=DEFAULT_FILE_SIZE):
     63         """
     64         Set up local variables.
     65 
     66         @param dev: block device / file to test.
     67                 Spare partition on root device by default
     68         @param filesize: size of the file. 0 means whole partition.
     69                 by default, 1GB.
     70         """
     71         if dev != '' and (os.path.isfile(dev) or not os.path.exists(dev)):
     72             if filesize == 0:
     73                 raise error.TestError(
     74                     'Nonzero file size is required to test file systems')
     75             self.__filename = dev
     76             self.__filesize = filesize
     77             self.__description = ''
     78             return
     79 
     80         if not dev:
     81             dev = utils.get_fixed_dst_drive()
     82 
     83         if dev == utils.get_root_device():
     84             if filesize == 0:
     85                 raise error.TestError(
     86                     'Using the root device as a whole is not allowed')
     87             else:
     88                 self.__filename = utils.get_free_root_partition()
     89         elif filesize != 0:
     90             # Use the first partition of the external drive
     91             if dev[5:7] == 'sd':
     92                 self.__filename = dev + '1'
     93             else:
     94                 self.__filename = dev + 'p1'
     95         else:
     96             self.__filename = dev
     97         self.__get_disk_size()
     98         self.__get_device_description()
     99 
    100         # Restrict test to use a given file size, default 1GiB
    101         if filesize != 0:
    102             self.__filesize = min(self.__filesize, filesize)
    103 
    104         self.__verify_only = False
    105 
    106         logging.info('filename: %s', self.__filename)
    107         logging.info('filesize: %d', self.__filesize)
    108 
    109     def run_once(self, dev='', quicktest=False, requirements=None,
    110                  integrity=False, wait=60 * 60 * 72):
    111         """
    112         Runs several fio jobs and reports results.
    113 
    114         @param dev: block device to test
    115         @param quicktest: short test
    116         @param requirements: list of jobs for fio to run
    117         @param integrity: test to check data integrity
    118         @param wait: seconds to wait between a write and subsequent verify
    119 
    120         """
    121 
    122         if requirements is not None:
    123             pass
    124         elif quicktest:
    125             requirements = [
    126                 ('1m_write', []),
    127                 ('16k_read', [])
    128             ]
    129         elif integrity:
    130             requirements = [
    131                 ('8k_async_randwrite', []),
    132                 ('8k_async_randwrite', [self.VERIFY_OPTION])
    133             ]
    134         elif dev in ['', utils.get_root_device()]:
    135             requirements = [
    136                 ('surfing', []),
    137                 ('boot', []),
    138                 ('login', []),
    139                 ('seq_read', []),
    140                 ('seq_write', []),
    141                 ('16k_read', []),
    142                 ('16k_write', []),
    143                 ('1m_stress', []),
    144             ]
    145         else:
    146             # TODO(waihong@): Add more test cases for external storage
    147             requirements = [
    148                 ('seq_read', []),
    149                 ('seq_write', []),
    150                 ('16k_read', []),
    151                 ('16k_write', []),
    152                 ('1m_stress', []),
    153             ]
    154 
    155         results = {}
    156         for job, options in requirements:
    157             # Keys are labeled according to the test case name, which is
    158             # unique per run, so they cannot clash
    159             if self.VERIFY_OPTION in options:
    160                 time.sleep(wait)
    161                 self.__verify_only = True
    162             else:
    163                 self.__verify_only = False
    164             env_vars = ' '.join(
    165                 ['FILENAME=' + self.__filename,
    166                  'FILESIZE=' + str(self.__filesize),
    167                  'VERIFY_ONLY=' + str(int(self.__verify_only))
    168                 ])
    169             job_file = os.path.join(self.bindir, job)
    170             results.update(fio_util.fio_runner(self, job_file, env_vars))
    171 
    172         # Output keys relevant to the performance, larger filesize will run
    173         # slower, and sda5 should be slightly slower than sda3 on a rotational
    174         # disk
    175         self.write_test_keyval({'filesize': self.__filesize,
    176                                 'filename': self.__filename,
    177                                 'device': self.__description})
    178         logging.info('Device Description: %s', self.__description)
    179         self.write_perf_keyval(results)
    180         for k, v in results.iteritems():
    181             if k.endswith('_error'):
    182                 self._fail_count += int(v)
    183         if self._fail_count > 0:
    184             raise error.TestFail('%s failed verifications' %
    185                                  str(self._fail_count))
    186