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