Home | History | Annotate | Download | only in dma_memtest
      1 import os, time, re, subprocess, shutil, logging
      2 from autotest_lib.client.bin import utils, test
      3 from autotest_lib.client.common_lib import error
      4 
      5 
      6 class dma_memtest(test.test):
      7     """
      8     A test for the memory subsystem against heavy IO and DMA operations,
      9     implemented based on the work of Doug Leford
     10     (http://people.redhat.com/dledford/memtest.shtml)
     11 
     12         @author Lucas Meneghel Rodrigues (lucasmr (at] br.ibm.com)
     13         @author Rodrigo Sampaio Vaz (rsampaio (at] br.ibm.com)
     14     """
     15     version = 1
     16     def initialize(self):
     17         self.cachedir = os.path.join(self.bindir, 'cache')
     18         self.nfail = 0
     19 
     20 
     21     def setup(self, tarball_base='linux-2.6.18.tar.bz2', parallel=True):
     22         """
     23         Downloads a copy of the linux kernel, calculate an estimated size of
     24         the uncompressed tarball, use this value to calculate the number of
     25         copies of the linux kernel that will be uncompressed.
     26 
     27             @param tarball_base: Name of the kernel tarball location that will
     28             be looked up on the kernel.org mirrors.
     29             @param parallel: If we are going to uncompress the copies of the
     30             kernel in parallel or not
     31         """
     32         if not os.path.isdir(self.cachedir):
     33             os.makedirs(self.cachedir)
     34         self.parallel = parallel
     35 
     36         kernel_repo = 'http://www.kernel.org/pub/linux/kernel/v2.6'
     37         tarball_url = os.path.join(kernel_repo, tarball_base)
     38         tarball_md5 = '296a6d150d260144639c3664d127d174'
     39         logging.info('Downloading linux kernel tarball')
     40         self.tarball = utils.unmap_url_cache(self.cachedir, tarball_url,
     41                                              tarball_md5)
     42         size_tarball = os.path.getsize(self.tarball) / 1024 / 1024
     43         # Estimation of the tarball size after uncompression
     44         compress_ratio = 5
     45         est_size = size_tarball * compress_ratio
     46         self.sim_cps = self.get_sim_cps(est_size)
     47         logging.info('Source file: %s' % tarball_base)
     48         logging.info('Megabytes per copy: %s' % size_tarball)
     49         logging.info('Compress ratio: %s' % compress_ratio)
     50         logging.info('Estimated size after uncompression: %s' % est_size)
     51         logging.info('Number of copies: %s' % self.sim_cps)
     52         logging.info('Parallel: %s' % parallel)
     53 
     54 
     55     def get_sim_cps(self, est_size):
     56         '''
     57         Calculate the amount of simultaneous copies that can be uncompressed
     58         so that it will make the system swap.
     59 
     60             @param est_size: Estimated size of uncompressed linux tarball
     61         '''
     62         mem_str = utils.system_output('grep MemTotal /proc/meminfo')
     63         mem = int(re.search(r'\d+', mem_str).group(0))
     64         mem = int(mem / 1024)
     65 
     66         # The general idea here is that we'll make an amount of copies of the
     67         # kernel tree equal to 1.5 times the physical RAM, to make sure the
     68         # system swaps, therefore reading and writing stuff to the disk. The
     69         # DMA reads and writes together with the memory operations that will
     70         # make it more likely to reveal failures in the memory subsystem.
     71         sim_cps = (1.5 * mem) / est_size
     72 
     73         if (mem % est_size) >= (est_size / 2):
     74             sim_cps += 1
     75 
     76         if (mem / 32) < 1:
     77             sim_cps += 1
     78 
     79         return int(sim_cps)
     80 
     81 
     82     def run_once(self):
     83         """
     84         Represents a single iteration of the process. Uncompresses a previously
     85         calculated number of copies of the linux kernel, sequentially or in
     86         parallel, and then compares the tree with a base tree, that was
     87         uncompressed on the very beginning.
     88         """
     89 
     90         parallel_procs = []
     91 
     92         os.chdir(self.tmpdir)
     93         # This is the reference copy of the linux tarball
     94         # that will be used for subsequent comparisons
     95         logging.info('Unpacking base copy')
     96         base_dir = os.path.join(self.tmpdir, 'linux.orig')
     97         utils.extract_tarball_to_dir(self.tarball, base_dir)
     98         logging.info('Unpacking test copies')
     99         for j in range(self.sim_cps):
    100             tmp_dir = 'linux.%s' % j
    101             if self.parallel:
    102                 os.mkdir(tmp_dir)
    103                 # Start parallel process
    104                 tar_cmd = ['tar', 'jxf', self.tarball, '-C', tmp_dir]
    105                 logging.debug("Unpacking tarball to %s", tmp_dir)
    106                 parallel_procs.append(subprocess.Popen(tar_cmd,
    107                                                        stdout=subprocess.PIPE,
    108                                                        stderr=subprocess.PIPE))
    109             else:
    110                 logging.debug("Unpacking tarball to %s", tmp_dir)
    111                 utils.extract_tarball_to_dir(self.tarball, tmp_dir)
    112         # Wait for the subprocess before comparison
    113         if self.parallel:
    114             logging.debug("Wait background processes before proceed")
    115             for proc in parallel_procs:
    116                 proc.wait()
    117 
    118         parallel_procs = []
    119 
    120         logging.info('Comparing test copies with base copy')
    121         for j in range(self.sim_cps):
    122             tmp_dir = 'linux.%s/%s' % (j,
    123                             os.path.basename(self.tarball).strip('.tar.bz2'))
    124             if self.parallel:
    125                 diff_cmd = ['diff', '-U3', '-rN', 'linux.orig', tmp_dir]
    126                 logging.debug("Comparing linux.orig with %s", tmp_dir)
    127                 p = subprocess.Popen(diff_cmd,
    128                                      stdout=subprocess.PIPE,
    129                                      stderr=subprocess.PIPE)
    130                 parallel_procs.append(p)
    131             else:
    132                 try:
    133                     logging.debug('Comparing linux.orig with %s', tmp_dir)
    134                     utils.system('diff -U3 -rN linux.orig linux.%s' % j)
    135                 except error.CmdError, e:
    136                     self.nfail += 1
    137                     logging.error('Error comparing trees: %s', e)
    138 
    139         for proc in parallel_procs:
    140             out_buf = proc.stdout.read()
    141             out_buf += proc.stderr.read()
    142             proc.wait()
    143             if out_buf != "":
    144                 self.nfail += 1
    145                 logging.error('Error comparing trees: %s', out_buf)
    146 
    147         # Clean up for the next iteration
    148         parallel_procs = []
    149 
    150         logging.info('Cleaning up')
    151         for j in range(self.sim_cps):
    152             tmp_dir = 'linux.%s' % j
    153             shutil.rmtree(tmp_dir)
    154         shutil.rmtree(base_dir)
    155 
    156 
    157     def cleanup(self):
    158         if self.nfail != 0:
    159             raise error.TestError('DMA memory test failed.')
    160         else:
    161             logging.info('DMA memory test passed.')
    162